friend

package
v1.3.16 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2026 License: MIT Imports: 46 Imported by: 0

Documentation

Index

Constants

View Source
const (
	AntigravityPoll       = 500 * time.Millisecond
	AntigravityLandBudget = 30 * time.Second
)

AntigravityPoll is how often the mailbox is read while waiting for the sent message to appear in it, and AntigravityLandBudget how long: past it the message agentapi took is in the ledger as delivered, its id read off the mailbox when it lands (Follow).

View Source
const (
	PongCheck = "session check" // the presence's nonce (SessionCheckText)
	PongWake  = "wake"          // the coordinator's challenge nonce (a wake turn, an idle wake, a turn's head)
)

The kinds of pong request (PongRequest): each its own series of nonces.

View Source
const (
	TmuxPoll         = 500 * time.Millisecond
	TmuxAcceptWithin = time.Minute
)

Timing of a typed delivery: the pane is polled each TmuxPoll for up to TmuxAcceptWithin for the prompt line to go (the turn started).

View Source
const (
	AntigravityFollowEvery = 10 * time.Second
	AntigravityReadBound   = SessionBound
	AntigravityStopped     = 3
	AntigravitySwitchHold  = 10 * time.Minute
	AntigravityKeptRead    = 64
)

The ledger's rules. AntigravityReadBound is the check period: a delivery unread past it, with nothing read since, is a session down (Deliver refuses). A conversation that has left AntigravityStopped deliveries unread past it, and read nothing since the oldest, stopped reading; delivery moves to a conversation that read a delivery since, and never moves again within AntigravitySwitchHold. Follow looks every AntigravityFollowEvery. AntigravityKeptRead bounds the deliveries kept once read or sent again (the newest); one unread is never dropped.

View Source
const (
	VerdictOK     = "ok"
	VerdictBroken = "broken"
	VerdictDown   = "down"
	VerdictUntrue = "untrue"
)

Verdict constants for the friend health check.

View Source
const (
	StageDeliver = "deliver"
	StageAct     = "act"
	StageReply   = "reply"
)

The stages of the delivery check, in the order they are passed: the adapter took the text (deliver), the session ran the pong line it carries and the pong is on the bus (act; reply is the bus log not read).

View Source
const (
	DefaultCheckWithin = SessionBound
	CheckPoll          = time.Second
)

DefaultCheckWithin is how long the check waits for the session's pong by default: the session check's own bound (SessionBound). CheckPoll is how often it reads the bus and the pong file meanwhile.

View Source
const (
	RefusalRepeats = 3
	RefusalDownFor = 15 * time.Minute
)

RefusalRepeats is how many times in a row the server must refuse one verb before the daemon raises it (a down beat and one word to the coordinator), and RefusalDownFor how long her beat says down with it while the server stays refused (a server that answers again clears it at once: docs/SPEC-FRIEND.md, "A refused verb").

View Source
const (
	SessionOK     = "ok"
	SessionBroken = "broken"
)

The session's state, as the status file says it.

View Source
const (
	FromCards = "friend cards"
	FromView  = "the worker view"
)

The sources of a Row.

View Source
const (
	FriendCardsVerb = "friend cards"
	FriendBeatVerb  = "friend beat"
)

The server verbs the daemon counts refusals of, one key per verb in Status.Refused (docs/SPEC-FRIEND.md, "A refused verb").

View Source
const (
	ViewEvery   = 15 * time.Second
	ServedEvery = time.Minute
)

ViewEvery is how often the worker view is read while friend cards is refused: it runs on the server's line (serveView takes the tick's lock), so it is read about as often as friend sync's loop runs, never every loop. ServedEvery is how often friend cards is asked again.

View Source
const (
	NoGoRoot    = "/NO-GO-ON-THIS-MACHINE-run-it-on-a-bench-over-ssh"
	ShimDirName = "shims"
)

The go refusal: no go command runs on the machine that runs the lanes (the owner's rule: it is not a build machine). A lane's PATH carries go and gofmt that refuse, and GOROOT points nowhere.

View Source
const (
	FaultRepeats = 3
	FaultWithin  = 10 * time.Minute
	FaultDownFor = 15 * time.Minute
)

The bound on a harness fault: the same fault FaultRepeats times within FaultWithin on one row marks the row down for FaultDownFor, once, with one judgment to the seat.

View Source
const (
	ModeBatch   = "batch"
	ModeOneShot = "one-shot"
)

The delivery modes (pkg/config FriendModes, the friend row's mode).

View Source
const (
	InstantFail        = 5 * time.Second
	InstantFailRepeats = 2
)

InstantFail is how fast a card run that ends with no REPORT.md and no RESULT.md marks a broken harness rather than the card's own failed attempt: on 2026-10-11 an unsigned config directory ended every claude -p run terminal_reason api_error in about a second. InstantFailRepeats such runs in a row stop the lane's taking and re-prove the harness before any take, instead of finishing a third card FAILED (docs/SPEC-FRIEND.md, "A lane proves its harness").

View Source
const (
	ReleasePoll        = 250 * time.Millisecond
	DefaultExitTimeout = 20 * time.Second
)

ReleasePoll is how often Install asks launchd, after the bootout, whether it still holds the old service; DefaultExitTimeout is how long it asks, the launchd default for a plist with no ExitTimeOut (the time launchd gives a daemon to exit before it kills it).

View Source
const (
	KindLimit   = "limit"
	KindCredits = "credits"
)

The kinds of a limit: the harness's usage window is spent, or the balance that pays for it is empty.

View Source
const (
	LaneMarkEvery = 30 * time.Second
	LaneMarkStale = 5 * LaneMarkEvery
)

LaneMarkStale is how long a running mark stands unrefreshed: a lane refreshes its mark every LaneMarkEvery while its card runs, so one older than this is a lane gone with its daemon, and a new lane may claim the card.

View Source
const (
	WindowFiveHour = "five_hour"
	WindowSevenDay = "seven_day"
)

The windows a Claude subscription reports, in its own spelling.

View Source
const (
	SessionBound       = 5 * time.Minute
	SessionCheckPrefix = "SESSION CHECK "
	LogBatch           = 1000 // log entries read per step; the rest the next
)

Whether a friend is there is the nova-hook record's alone: nova-hook serve challenges every hooked name once a minute, only the model answers with nova-hook prove, and two misses unhook it (docs/SPEC-HOOK.md). What is left here is the delivery check's (nova-friend check --harness, Conformance): a SESSION CHECK carrying a nonce goes in through the harness, and the session's pong with that nonce must reach the bus within SessionBound.

View Source
const (
	PresenceUp   = "up"
	PresenceDown = "down"
)

The presence, as check reports it from the nova-hook record (HookRecord.Up).

View Source
const (
	RateBackoffFirst = 30 * time.Second // the first pause after a rate limit
	RateBackoffMax   = 10 * time.Minute // the pause doubles up to this
	RateRaiseEvery   = 10 * time.Minute // a clean stretch this long raises the cap one lane
	RateJudgeWithin  = time.Hour        // RateJudgeAfter lowerings within this are one judgment
	RateJudgeAfter   = 3
)

The lane governor's numbers.

View Source
const (
	OneThingLeft     = "THE ONE THING LEFT: "
	ReaderFoundLabel = "The reader found: "
)

The labels of a reworked brief's first lines, and of the server's lines it is made from.

View Source
const (
	HeldByDaemon     = "the daemon"
	HeldByFriendSync = "friend sync"
)

The holders of an unaddressed LAND, as UnaddressedText names them.

View Source
const (
	KindDir     = "directory"
	KindFile    = "file"
	KindMissing = "missing"
	KindSymlink = "symlink" // shown as "symlink to <target>"
)

The kinds a path is, as a setting shows it.

View Source
const (
	JobsDir    = "jobs"
	MirrorsDir = "mirrors"
	JobFile    = "JOB.md"
)

The directories under her working directory that staging writes.

View Source
const (
	StageRetryEvery   = time.Minute
	MirrorFreshFor    = 10 * time.Second
	MirrorCloneBudget = 30 * time.Minute
)

StageRetryEvery is how long a job whose stage failed waits before it is staged again; a judgment stands, said once, until a stage of its repository or card succeeds. MirrorFreshFor is how long a fetched mirror serves stages without fetching again, so a burst of cards on one repository is one fetch. MirrorCloneBudget bounds a mirror's fetch: the first, of the whole repository, is the one slow step (every later one fetches only what is new).

View Source
const (
	FinishedJobsKept = 8
	PrunePerPass     = 4
)

FinishedJobsKept is how many finished jobs' worktrees the daemon's cleanup keeps, the newest staged; the rest are pruned (Stager.Prune), at most PrunePerPass a cleanup, so the loop that runs it is held a few seconds at most.

View Source
const (
	StatusFile = "status.json"
	LogFile    = "deliver.log"
	WatchFile  = "watch.json"
	QueueFile  = "inbox/QUEUE.json"
)

The friend's files, one writer each. The state files live in the state directory (DefaultStateDir under the home directory, or --state-dir), never on the friend's volume: a background process on this platform may not touch a removable volume without the person's permission (measured 2026-10-04, "operation not permitted" on the mkdir). The daemon writes Status and the log. The queue file is the coordinator's and the session's, under the working directory (SPEC-FRIEND.md, the files).

View Source
const (
	StatusEvery = 5 * time.Second
	DaemonStale = 30 * time.Second
)

DaemonStale is how old the status file may be while the daemon counts as up: it rewrites the file at least every StatusEvery.

View Source
const AccessibilityCheckScript = "use framework \"ApplicationServices\"\nreturn (current application's AXIsProcessTrusted() as boolean) as text\n"

AccessibilityCheckScript asks whether this process is trusted. It calls AXIsProcessTrusted and never AXIsProcessTrustedWithOptions, so it cannot prompt.

View Source
const AccessibilityRemedy = "grant Accessibility to this binary in System Settings, Privacy and Security, Accessibility; nova-friend does not ask"

AccessibilityRemedy is what a person does when the window step cannot type. The permission is granted to this binary. The tool never asks for it.

View Source
const ActivityEvery = 10 * time.Second

ActivityEvery is how often the daemon walks: the beat goes every BeatEvery and carries the last walk's answer in between.

View Source
const AntigravityData = ".gemini/antigravity"

AntigravityData is the harness's app data directory, under the home.

View Source
const AntigravityLedgerFile = "antigravity-ledger.json"

AntigravityLedgerFile is the ledger's file in the daemon's state directory.

View Source
const AntigravityOpen = "" /* 143-byte string literal not displayed */

AntigravityOpen is what a friend whose Antigravity refuses does.

View Source
const AntigravityTitle = "nova-friend"

AntigravityTitle heads every message nova-friend sends.

View Source
const BeatEvery = time.Second

BeatEvery is the cadence of the daemon's sprint metadata beat and lane loop. It carries lane configuration and activity, never session liveness.

View Source
const BootstrapTries = 5

BootstrapTries is how many times a bootstrap is sent while launchd is still tearing the old agent down (it answers EIO, "Input/output error", for a second or so after the bootout, measured 2026-10-04).

View Source
const CapTailLines = 40

CapTailLines is how many of the lane's last output lines a capped card's report quotes.

View Source
const CardTurns = 2

CardTurns is how many turns a one-shot lane gives one card: a turn that ends without the card's RESULT.md hands the same card again once, then the card is reported to the coordinator and set aside.

View Source
const CodexPushUnavailable = "" /* 378-byte string literal not displayed */

CodexPushUnavailable is the refusal for implicit Codex delivery (SPEC-FRIEND.md, Codex). No flag or environment variable enables queue or exec-resume as push.

View Source
const CodexWritableRoots = "sandbox_workspace_write.writable_roots"

CodexWritableRoots is the setting in CODEX_HOME/config.toml that lets a workspace-write sandbox write the friend's directory: [sandbox_workspace_write] writable_roots. The friend's directory is written there by its real path; a symlink there took no writes for ten hours on 2026-10-05.

View Source
const DSHPreset = "standard"

DSHPreset is the agent preset a friend's DeepSeek Harness opens new sessions under. A session under "minimal" makes every headless turn exit 1 before any write (measured 2026-10-04 and 2026-10-05, docs/SPEC-FRIEND.md, the dsh row); the owner set the desktop profile's selected default to "standard" by hand on 2026-10-04, and install now writes it. A session keeps the preset it was opened under: one opened under minimal stays so.

View Source
const (
	DSHPresetEntry = "agent-preset-registry"
)

The patch entry that holds it: the desktop profile's patch layer, DSH_HOME/profiles/desktop/cordis.patch.yml, a YAML list of loader patch entries; the preset registry's config keys default and selectedDefault.

View Source
const DSHProgram = "/Applications/DeepSeek Harness.app/Contents/Resources/runtime/cli/bin/dsh"

DSHProgram is where the DeepSeek Harness desktop app ships its CLI on this platform; the app installs no link on PATH.

View Source
const DefaultBrokenAfter = 3

DefaultBrokenAfter is how many turns in a row the provider must refuse with the same reason before the session is broken (--broken-after).

View Source
const DefaultLimitWait = time.Hour

DefaultLimitWait is how long a harness that refused at its limit and named no reset is down before a wake is tried.

View Source
const DefaultLoadWidth = 3

DefaultLoadWidth is how many lanes run while the load is above the row's bound.

View Source
const DefaultPacing = 0.80

DefaultPacing is the fraction of each subscription window the sprint may spend when the row names no pacing.

View Source
const DefaultReadSlots = 2

DefaultReadSlots is how many reads a friend runs at once when her row says none.

View Source
const DefaultSilentStop = 20 * time.Minute

DefaultSilentStop is how long a turn may print nothing before the daemon stops it (--silent-stop): a turn that prints keeps running however long it takes (the finding of 2026-10-04: a fixed ten-minute cap killed a friend's real work mid-turn).

View Source
const DefaultSpendCapUSD = 20.0

DefaultSpendCapUSD is the spend cap in USD per day per provider when the config names none.

View Source
const DefaultTokenCap int64 = 6_000_000

DefaultTokenCap is a card's token cap where the friend row names none.

View Source
const FinishWait = 10 * time.Second

FinishWait bounds a lane's finish to the sprint server; one not answered is left to friend sync, which reads the REPORT.md the lane wrote.

View Source
const HarnessFaultEnvironment = "harness-fault: environment"

HarnessFaultEnvironment is the fault of a lane run the friend's environment broke, never the card's: it ended within InstantFail with no REPORT.md and no RESULT.md and its output names a filesystem error on a path outside its lane.

View Source
const HarnessFaultNoReport = "harness-fault: no report"

HarnessFaultNoReport is the fault of a lane run that exited 0 and left no REPORT.md or RESULT.md: the harness's, never the worker's failed attempt.

View Source
const HoldersBudget = 10 * time.Second

HoldersBudget bounds the current ownership view asked for an old report.

View Source
const HookProven = "proven"

The hook record's state a friend is up in (nova-tools hook.StateProven).

View Source
const HostFile = "host.json"

HostFile is the friend's host state in its state directory.

View Source
const InboxEvery = BeatEvery

InboxEvery is how often the daemon reconciles her inbox with her row: every loop.

View Source
const KillDelay = 5 * time.Second

KillDelay is how long a signalled group gets to end before SIGKILL.

View Source
const LaneFaultAlike = 2

LaneFaultAlike is how many identical provider refusals in a row mark the lane faulted: a guard's denial is terminal, never a retry each minute, and the second identical one is the judgment (docs/SPEC-FRIEND.md, the lanes).

View Source
const LaneFaultFile = "lane-fault"

LaneFaultFile is the lane's provider-refusal fault report in the state directory (lane-fault=<n> refusal="<first line>"), written by the daemon when a lane faults and removed when the fault is cleared; absent is no fault (docs/SPEC-FRIEND.md, the lanes).

View Source
const LaneIdleAsk = 15 * time.Second

LaneIdleAsk is how long a lane the server gave nothing waits before it asks again.

View Source
const LaneMarkFile = "LANE"

LaneMarkFile is a job's lane mark, under jobs/<job>/.

View Source
const LaneOpenRetry = time.Minute

LaneOpenRetry is how long a lane whose session could not be opened waits before it tries again.

View Source
const LaneTmpName = "tmp"

LaneTmpName is the temporary directory inside a lane's own job directory.

View Source
const LimitFile = "limit.json"

LimitFile is the friend's limit, in the state directory: the provider's limit the session is at and when it resets. The daemon is its one writer; no file is no limit.

View Source
const LimitTail = 2048

LimitTail is how much of the end of a command's output is read for a limit line: a harness says it last, and the body of a reply that only talks about limits is far from the tail.

View Source
const MachineStoppedWord = "STOPPED"

MachineStoppedWord is the machine's state word that cancels jobs.

View Source
const MaxFixKeyWords = 8

MaxFixKeyWords bounds the key words a report is grepped for: a long fix is checked by its first ones.

View Source
const NoticeLimit = 256

NoticeLimit bounds unsent coordinator messages in one daemon. Episode detectors enqueue once per transition; this queue only retries delivery (tla/FriendNotices.tla, Queue and Send; tla/BusSendOnce.tla).

View Source
const OpenCodeConfig = "opencode.json"

OpenCodeConfig is the project config file opencode reads in its directory.

View Source
const OutboxRetry = time.Minute

OutboxRetry is how long a finish the server did not answer, or refused, waits before it is sent again; the report stays where it is, and friend sync may finish it first.

View Source
const OutputKept = 2048

OutputKept bounds how much of a turn's output the daemon keeps in its record: the head, enough to see what the session did with the message.

View Source
const PauseBeatAhead = time.Hour

PauseBeatAhead is how far ahead the friend's down beat sets its --until while the pause marker stands. Every beat sends it again, so it never lapses while she is paused; once a person clears the marker the next beat goes without --until and withdraws it.

View Source
const PauseFile = "PAUSED"

PauseFile is the marker in the state directory that holds the lanes down: it outlives the daemon, and nothing resumes until a person removes it (nova-friend resume).

View Source
const PongSubject = "pong"

PongSubject is the subject of a session's pong on the bus (nova-friend pong): the answer to a reach step or a delivery check, never liveness.

View Source
const ProgressEvery = 3 * time.Minute

ProgressEvery is how often the daemon stamps progress on a card whose lane turn prints: the sprint's own number (internal/sprint ProgressEvery, inside the late rule's ten-minute window; docs/SPEC-SPRINT.md section 8, the rules table's row late).

View Source
const ReadAskEvery = 10 * time.Second

ReadAskEvery is how often the daemon asks its reader queue, which is the reader's beat.

View Source
const ReadBodyKept = 3500

ReadBodyKept bounds the body of a read's RESULT.md recorded as its finding.

View Source
const ReportCap = 64 * 1024

ReportCap bounds the REPORT.md the daemon reads (friend sync's own cap): a verdict, a head and 600 characters need far less, and a larger report is noted and never read whole.

View Source
const ReportChars = 600

ReportChars is how much of a failed report rides on its finish.

View Source
const ReportWait = 2 * time.Minute

ReportWait is how long a lane the server says holds a card whose outbox has the report of a run before that no outbox step can ever finish (a RESULT.md alone, a REPORT.md with no Verdict: a run cut between its writes) waits before it finishes it failed itself. A report with a Verdict is the outbox step's alone, whatever it waits on (a LAND's head not yet origin's tip, a tip that cannot be read, a finish not taken): the lane never finishes it.

View Source
const RetiredDir = "inbox/retired"

RetiredDir is where a job whose card left her row goes, under her working directory.

View Source
const RetiredEpochs = "retired"

RetiredEpochs is the folder under the friend's working directory that keeps the items the epochs retired, named for its epoch: retired/epoch-<n>/inbox/<job> and retired/epoch-<n>/outbox/<job>.

View Source
const RunnerLogCap = 4 << 20

RunnerLogCap bounds the runner log the daemon reads: its last RunnerLogCap bytes.

View Source
const SeatCacheFor = 10 * time.Second

SeatCacheFor is how long the daemon keeps the seat holder it read from the sprint server: the authority of a message is never older than this (docs/SPEC-FRIEND.md, bus-authority-labels.w3).

View Source
const ServedBy = "daemon-writes-every-taken-card3"

ServedBy is the card that adds friend cards to the sprint server: a daemon whose server refuses the verb names it, so whoever reads the line knows which server change is missing.

View Source
const SessionLimited = "limited"

SessionLimited is the status file's session while the harness is down at its limit: `session=limited kind=<k> until=<RFC3339>`.

View Source
const StatusErrorEvery = time.Minute

StatusErrorEvery bounds reports of an unwritable daemon status file.

View Source
const StopReturnReason = "owned process stopped"

StopReturnReason is the reason every stop-return names.

View Source
const StopReturnRetry = StatusErrorEvery

StopReturnRetry is how long a refused stop-return waits before it is sent again: the store's verb may be absent, and the lane step runs every second.

View Source
const StopReturnsKept = 200

StopReturnsKept bounds the stop-returns the lane state keeps once sent: the record of the acks, newest last.

View Source
const TailKept = 64 << 10

TailKept bounds the output a lane's turn keeps for its tail: its last TailKept bytes.

View Source
const TakeRetry = 5 * time.Second

TakeRetry is how long a lane waits after a refused ask, or an answer it cannot run yet (its brief not in her inbox, its job not staged), before it asks again.

View Source
const TakeWait = 10 * time.Second

TakeWait bounds one ask to the sprint server.

View Source
const TipBudget = 10 * time.Second

TipBudget bounds the one ls-remote of Tip.

View Source
const TmuxPrefix = "friend-"

TmuxPrefix starts the name of every hosted session: friend-<name>.

View Source
const TokenPoll = 15 * time.Second

TokenPoll is how often a lane reads a running card's usage where the harness prints none as it runs (an OpenCode turn: its session's export).

View Source
const TokenPollEvery = 15 * time.Second

TokenPollEvery is how often a running card's tokens are read for the cap.

View Source
const ViewWhy = "the server does not serve friend cards, and the worker view names the card and its job with no brief"

ViewWhy is why a card the worker view names has no brief written for it.

View Source
const WallVerb = "wall"

WallVerb is nova-friend's verb that runs one command inside a lane's wall: `nova-friend wall --profile <p> --dir <d> --deny <self>... [--config-dir <c>] [--job <j>]... [--read <r>]... -- <command> <args>` (docs/SPEC-FRIEND.md, buds-in-the-wall-r.w5).

Variables

View Source
var ActivityRoots = []string{"outbox", "inbox", "jobs", "."}

ActivityRoots are the places the walk reads, in this order, under her working directory: the outbox (her results), the inbox (the cards she was dealt), then the jobs and the rest of the directory, so a large clone cannot use the bound up before the outbox is read.

View Source
var AllPronouns = []Pronouns{SheHer, HeHim, TheyThem}

AllPronouns is every set a row may name.

View Source
var AppBundles = map[string]string{
	"claude":      "com.anthropic.claudefordesktop",
	"codex":       "com.openai.codex",
	"cursor":      "com.todesktop.230313mzl4w4u92",
	"antigravity": "com.google.antigravity",
	"windsurf":    "com.exafunction.windsurf",
	"zed":         "dev.zed.Zed",
	"warp":        "dev.warp.Warp-Stable",
}

AppBundles is the bundle identifier the window step looks up for a GUI harness. These are lookup ids, not a measured survey of installed apps. A harness with no entry, and not tmux, has no window.

View Source
var ClaudeTrim = []string{"--strict-mcp-config", "--disable-slash-commands", "--no-chrome", "--tools", "Bash", "Read", "Write", "Edit", "Grep", "Glob"}

ClaudeTrim is the trimmed call of a headless Claude Code run (the owner's finding of 2026-10-04: no MCP servers, no slash commands, no browser, six tools, which cut the context of each call from about 50k tokens to 12.7k). --tools takes every argument after it, so it is last, after the prompt.

View Source
var DefaultActivityLimits = ActivityLimits{Files: 2000, Time: 50 * time.Millisecond}

DefaultActivityLimits is what the daemon walks with: two thousand files, fifty milliseconds.

View Source
var DefaultLaneCaps = map[string]time.Duration{
	"flash":    15 * time.Minute,
	"pro":      45 * time.Minute,
	"heavy":    90 * time.Minute,
	"frontier": 150 * time.Minute,
}

DefaultLaneCaps is the wall cap of a lane's card by its tier, where the row names none.

View Source
var ErrBinaryOnRemovableVolume = errors.New("binary on a removable volume: launchd starts it and it does nothing")

ErrBinaryOnRemovableVolume is the refusal when the agent's binary is on a removable volume and cannot be copied under the home (docs/SPEC-FRIEND.md). launchd starts that binary and it does nothing.

View Source
var ErrLanguageServerNotRunning = errors.New("no antigravity language server is running: is Antigravity open?")

ErrLanguageServerNotRunning is returned when no Antigravity language server is running.

View Source
var ErrNoConfigDir = errors.New("harness claude wants --config-dir (or CLAUDE_CONFIG_DIR): the friend's own Claude config directory, written into the agent")

ErrNoConfigDir is the refusal of claude settings with no config directory: a friend on Claude Code has her own CLAUDE_CONFIG_DIR, and install writes it into the agent (run --config-dir) rather than a unit carrying it by hand.

View Source
var ErrNoSession = errors.New("no grok session is open")

ErrNoSession is WakeOf's refusal when no grok window is open in the directory.

View Source
var ErrNotDue = errors.New("not due")

ErrNotDue is a Held that did not ask this pass (the worker view is read once a ViewEvery): nothing is reconciled and nothing is said.

View Source
var ErrNotRealDir = errors.New("not a real directory")

ErrNotRealDir is the refusal when a path a harness setting names is a symlink or not a directory: a harness that resolves it works elsewhere or refuses (Codex took no writes into a symlinked writable root, 2026-10-05). Install never replaces a symlink; the remedy is to name the real path.

View Source
var ErrSessionInNoDB = errors.New("no opencode database read holds the session")

ErrSessionInNoDB is a session that none of the databases read holds: its tokens are unread, never zero.

View Source
var GoShimNames = []string{"go", "gofmt"}

GoShimNames are the commands the shims stand in for.

View Source
var Harnesses = append([]string{"opencode", "codex", "claude", "antigravity", "dsh", "gemini", "grok", "tmux"}, RefusedHarnesses...)

Harnesses are the harness names run and install take, in the order the help lists them; OpenCode, Codex, Antigravity, DSH, Gemini, Grok and tmux (a TUI hosted by nova-friend host) have a deliver command, the rest refuse honestly (Stub), the surveyed ones with their reason.

View Source
var HostPrompts = map[string]string{
	"opencode": `^\s*[┃>]?\s*Ask anything`,
	"grok":     genericPrompt,
	"aider":    `^(\S+ )?>\s*$`,
}

HostPrompts is each hostable harness's idle prompt pattern, matched against the last non-empty line of the captured pane. It is data: --prompt on host overrides it for a harness that draws its prompt differently.

View Source
var OpenCodeRunFlags = []string{"--session", "--model"}

OpenCodeRunFlags are the flags of `opencode run` the adapter passes: the session of a turn, and the model of a read. The directory is never one: it is the process's working directory.

View Source
var ReadModels = map[string]string{
	"frontier": "claude-fable-5-1",
	"heavy":    "claude-opus-5-5",
	"pro":      "claude-sonnet-5-5",
	"flash":    "claude-haiku-5-5",
}

ReadModels is the model of each tier for a claude account's reads, the table the bud runners carried. It is the one claude tier map: nova-friend's ReadModel hook reads it for every claude lane. The owner's rule (2026-10-11): each tier runs the newest model of its class, flash haiku, pro sonnet, heavy opus, frontier fable, and never a bigger class (read_lanes_models_test.go pins both).

View Source
var Refusals = map[string]string{
	"copilot":  "not installed here; the route is `copilot -p <text> --resume <id>`, after `copilot login`",
	"cursor":   "not installed here; the route is `agent -p --resume <chatId> <text>`, after `agent login`",
	"amp":      "not installed here; the route is `amp -x --thread-id <id> <text>`, with AMP_API_KEY",
	"goose":    "not installed here; the route is `goose run -n <name> -r -t <text>`, with a provider key",
	"kiro":     "not installed here; the route is `kiro-cli chat --resume-id <id> <text>`, after `kiro-cli login`",
	"cline":    "not installed here; the route is `cline --id <session> <text>` to the hub at 127.0.0.1:25463, after `cline auth`",
	"aider":    "no route: aider keeps no session to adopt, only a history file replayed into a new process",
	"roo":      "no route: Roo Code's sendMessage API lives inside VS Code, reachable only from another extension",
	"windsurf": "no route: nothing documented reaches a running Cascade conversation from outside the app",
	"zed":      "no route: Zed is an ACP client; nothing documented reaches a running thread from outside",
	"warp":     "no route: `oz run message send` reaches cloud runs only; `oz agent run` starts a new run",
}

Refusals are the harnesses of the survey of 2026-10-04 (SPEC-FRIEND.md, the harness survey) with no adapter: each the one-line reason, the route the vendor documents where there is one, and what would make it real. None of them is installed on this machine; one with a route gets its adapter the day its binary and login are there.

View Source
var RefusedHarnesses = []string{"copilot", "cursor", "amp", "goose", "kiro", "cline", "aider", "roo", "windsurf", "zed", "warp"}

RefusedHarnesses lists Refusals in a fixed order, for the registry.

Functions

func AIPath added in v1.2.14

func AIPath(root, name string) string

AIPath is the working root of an AI name on a machine: every path nova tools build for an AI sits under <root>/ai/<name>/, its working directory being <root>/ai/<name>/working (docs/FRIENDS.md, the ai/ layout).

func AdapterRemedy

func AdapterRemedy(harness string) string

AdapterRemedy is what a friend on a harness with no deliver command does: the adapter card that gives it one, or a harness that has one.

func AgeString

func AgeString(d time.Duration) string

AgeString returns a formatted age string (e.g. "5s", "1m2s").

func AgentAPI

func AgentAPI(out string) (json.RawMessage, error)

AgentAPI reads what agentapi printed: its response, or its error (printed at exit 0 like a response).

func Ago

func Ago(d time.Duration) string

Ago is a duration as a person reads it on the table: 40s, 12m, 3h5m, 2d.

func AllowDirs

func AllowDirs(dir string, paths []string) (bool, error)

AllowDirs writes into dir's opencode.json that a tool call may touch each of paths and everything under it without a prompt (permission external_directory, a pattern map of path globs to allow), merged into what the file holds, written only when it changes. A headless opencode run auto-rejects a call that would prompt, and the turn ends there (measured 2026-10-04: a friend's working directory reached through its symlink in the home directory). It answers whether it wrote.

func AntigravityRunner added in v1.2.10

func AntigravityRunner(home string) string

AntigravityRunner is the agentapi program the antigravity adapter's lanes run, the wrapper the harness ships under the home (Home/.gemini/antigravity/bin/agentapi). It is the one path the adapter's agentapi runs and the acceptance run looks up, so the probe executes the daemon's own program (docs/SPEC-FRIEND.md, the acceptance run: the lane's own program). An empty home is the current user's.

func BenchRule

func BenchRule(friend, card string) string

BenchRule is the sentence every read prompt carries: the machine the friend runs on runs no go command, a Linux bench does.

func BriefFix

func BriefFix(brief string) string

BriefFix is the fix a brief's prelude asks of its attempt: its THE ONE THING LEFT line, else the server's 'The coordinator asks:' line, on one line; "" for a brief that asks none.

func BusLogEntries

func BusLogEntries(ctx context.Context, st bus.Store, friend string, since time.Time) (realCount int, lastReal time.Time, err error)

BusLogEntries reads the bus store log for real messages from friend since since (docs/SPEC-FRIEND.md "Check": the facts, bus).

func CappedWords

func CappedWords(limit time.Duration, tier string, overrun time.Duration) string

CappedWords is how a capped card's end names the cap, the words the sprint reads off its failed finish (internal/sprint ParseLaneCap): `capped at <cap> (tier <tier>, overrun <d>)`, overrun the card's wall past the cap when the lane ended it.

func CardText

func CardText(job LaneJob, n, width int, sendLine string) string

CardText is one lane turn: the card and its three steps, every path absolute, the brief's text inline (a model that reads a path relative still has it). No session bus message rides with the card.

func CheckFolderRoute

func CheckFolderRoute(harness, session, adapter, deliveryDir string) error

CheckFolderRoute keeps the harness and session identity independent of the delivery route. The target must already be watched; the daemon never makes it.

func ClaimLane

func ClaimLane(dir, job, who string, now time.Time) (holder string, err error)

ClaimLane claims the job's card for the lane who at now: the mark is created when there is none (only one creator can win), taken over when the one there is stale, and kept when it is who's own. holder is the lane that holds it instead ("" when claimed).

func ClaudeAsyncBashHook

func ClaudeAsyncBashHook(input []byte) []byte

ClaudeAsyncBashHook is the pure PreToolUse response for an opt-in Claude Code project hook (docs/CLAUDE-ASYNC-BASH-CANDIDATE.md). Every Bash call requests a native background task so an omitted timeout cannot hold the session when background tasks are enabled. The response changes only tool input; permission rules still decide whether the command may run.

func ClaudeInstallLine

func ClaudeInstallLine(harness, friend, wake string) string

ClaudeInstallLine is the NOTE install prints for harness claude; every other harness gets none.

func ClaudeProofArgs added in v1.2.12

func ClaudeProofArgs() []string

ClaudeProofArgs is claude's cheapest no-tool turn: one prompt, JSON out, one turn, no tools. The prompt travels as an argument, never parsed.

func ClaudeProofWhy added in v1.2.12

func ClaudeProofWhy(ctx context.Context, run Exec, dir, configDir, program string) string

ClaudeProofWhy runs claude's cheapest no-tool turn through run (the daemon's own exec, with CLAUDE_CONFIG_DIR=configDir) and answers why the harness cannot answer: "" when it can, else "harness claude cannot answer: <terminal_reason> (config dir <dir>)". A run that answers nothing, exits nonzero, or names a terminal reason that is not a normal end fails.

func ClaudeWaitLine

func ClaudeWaitLine(friend, wake string) string

ClaudeWaitLine is the one line a claude session runs as a background task (docs/SPEC-FRIEND.md, the Claude paragraph): the session's own blocking read of the bus, re-armed with the cursor it printed each time it returns. Claude Code has no command that puts a turn into a running session from outside; a background task's exit re-invokes the session. It is a command run once inside the session, not a flag, an environment variable or a wrapper at app start. wake is the file the daemon appends one line to per message, so a wait that missed nothing still returns.

func ClaudeWakePath

func ClaudeWakePath(stateDir, friend string) string

ClaudeWakePath is the wake file of a claude friend: <state>/<friend>.wake, in the daemon's state directory, named on the line the session runs.

func ClearLaneFault added in v1.2.14

func ClearLaneFault(stateDir string) (bool, error)

ClearLaneFault removes the lane fault file, the person's clear; whether one was there.

func ClearPause

func ClearPause(stateDir string) (bool, error)

ClearPause is a person bringing the lanes up: the marker removed; whether one was there.

func CodexAddRoot

func CodexAddRoot(text, root string) (string, error)

CodexAddRoot is the text with root added to writable_roots: the key's lines replaced by one line holding every root there and root, the key added under its table when absent, the table appended when absent.

func CodexRoots

func CodexRoots(text string) (roots []string, found bool, err error)

CodexRoots is writable_roots in a Codex config.toml's text; found is false when the key is absent.

func ContextFull added in v1.2.7

func ContextFull(reason string) bool

ContextFull says a provider refusal's reason is one of those wordings.

func CopyExecutable

func CopyExecutable(src, dst string) error

CopyExecutable copies src onto dst and keeps it executable. dst is replaced only after the copy is complete, so a failure leaves a previous dst in place (docs/SPEC-FRIEND.md).

func CostLine

func CostLine(t LaneTokens, rp RoutePrice, model string) (report, tokens, cost string)

CostLine is the Cost: line a REPORT.md carries under its Head: line, and the tokens: and cost: lines a RESULT.md does. The card is priced by the route row; opencode's own figure is kept beside it.

func CostOf

func CostOf(t cardcost.Tokens, rp RoutePrice, model string) string

CostOf prices a card's tokens at the route row, rounded up to the cent; with no row, or a sheet that cannot price them, "unpriced (<why>)": never a guess.

func DSHArgs

func DSHArgs(session string) []string

DSHArgs is the argument list of one delivery; the text travels on stdin ("-"), so it is never parsed as an argument.

func DSHNoPresetRemedy

func DSHNoPresetRemedy(dir string) string

DSHNoPresetRemedy is what a friend whose dsh session runs under an agent preset does: nova-friend install and run refuse that session with it (PushProof), since no delivery into it can succeed.

func DSHSessionKey

func DSHSessionKey(dir string) string

DSHSessionKey is the store's directory for the sessions of dir: the path with every separator a dash, one more dash in front and two behind (measured 2026-10-04 on the store: /Volumes/nova/ai/zhi is --Volumes-nova-ai-zhi--).

func DaemonStateDir

func DaemonStateDir(home, dir, friend string, mkdir func(string) error) (state, why string)

DaemonStateDir is where a daemon run with no --state-dir keeps its files: StateDirIn(dir), made by mkdir; when that is refused (a background process on macOS may not touch a removable volume without the person's permission), DefaultStateDir, and why names the refusal for the record.

func DeadLaneReport

func DeadLaneReport(friend, job, end string) string

DeadLaneReport is the REPORT.md the daemon writes for a dead lane: Verdict FAIL, and the runner's END line.

func DefaultDenySelf added in v1.2.7

func DefaultDenySelf(home, seat string) []string

DefaultDenySelf is the coordinator's self a lane wall denies when the daemon was given no --deny-self (nor NOVA_FRIEND_DENY_SELF): the seat's working root on this machine, <home>/ai/<seat>/working, which holds the seat's self repository and its copies. A wall that denies nothing is refused (sandbox bad_profile, exit 125 for every lane), so a friend installed without the flag must not run lane-less. nil when the seat is unknown or is no plain name; the wall then refuses as it always did.

func DefaultReadWork

func DefaultReadWork(dir string) (inboxCount, outboxCount int, newestName string, newestAt time.Time, err error)

DefaultReadWork reads directory entries of inbox/ and outbox/ under dir (docs/SPEC-FRIEND.md "Check": the facts, work).

func DefaultStateDir

func DefaultStateDir(home, friend string) string

DefaultStateDir is the home directory's state directory for friend, ~/.nova-friend/<friend>: where a daemon from before the state moved under --dir kept its files, and where one goes when its directory refuses them (DaemonStateDir).

func EndReport

func EndReport(friend string, lane int, c Card, end LaneEnd, head, branch string) string

EndReport is the REPORT.md a lane writes for a card whose run ended without one: HOLD with the pushed head when the friend's branch has one (friend sync keeps a HOLD's head when it is origin's tip), else FAIL; one paragraph naming the lane, how the run ended and the head. A card the lane ended at its cap is a HOLD, head or none, and its report quotes the last lines of the lane's output after the paragraph, each indented four spaces (lane_cap.go).

func ExitTimeout

func ExitTimeout(plist string) time.Duration

ExitTimeout is the plist's ExitTimeOut, DefaultExitTimeout when it has none.

func FaultDownText

func FaultDownText(friend string, p Pronouns, reason string, until time.Time) (subject, body string)

FaultDownText is the one judgment the seat is told when a row's lanes hit the same harness fault FaultRepeats times within FaultWithin: the subject and the body, naming the reason the row is down with and until when.

func FaultWords

func FaultWords(fault, first string) string

FaultWords is a harness fault as the record and the row say it: the fault and the harness's first error line ("the harness printed no error line" when it printed none).

func FindStateDir

func FindStateDir(home, dir, friend string) string

FindStateDir is where a reader looks for friend's state when no --state-dir names it: StateDirIn(dir) when a daemon has written its status there, else the home directory's (a daemon from before the move, or one whose directory refused it).

func FinishArgv

func FinishArgv(friend string, c Card, report, head, branch string) []string

FinishArgv is the sprint server's failed finish of a card whose run ended without the friend's report: `finish --as friend.<name> <card>@<gen> --epoch <n> --failed [--head <sha>] [--branch <b>] --report <text>`, the report as friend sync words it ("friend <name> <verdict>: <paragraph>").

func FinishNote

func FinishNote(friend, job, verdictLine, cost string, wall time.Duration) (subject, body string)

FinishNote is the bus note to the coordinator at a lane's finish: the subject and body.

func FixAddressed

func FixAddressed(report, fix string) (missing []string, ok bool)

FixAddressed says a report addresses fix: it names (case-insensitively, anywhere) at least half of the fix's key words, rounded up; a fix with none is addressed. missing is the key words it does not name.

func FixKeyWords

func FixKeyWords(fix string) []string

FixKeyWords is what a report on fix is grepped for: its distinct words of four or more letters, digits or underscores, lower case, the empty ones (fixStopWords) left out, the first MaxFixKeyWords of them.

func FriendCardsArgv

func FriendCardsArgv(friend string) []string

FriendCardsArgv is the worker verb the daemon sends the sprint server for her held cards.

func FundsJudgmentText

func FundsJudgmentText(friend string, p Pronouns, reason string) (subject, body string)

FundsJudgmentText is what the coordinator is told when a friend's provider is out of funds: one judgment.

func GeminiArgs

func GeminiArgs(session, text string) []string

GeminiArgs is the argument list of one delivery: the frame, apart from the transport.

func GitHubURL

func GitHubURL(repo string) string

GitHubURL is the clone URL of a repository named owner/name.

func GoRefusal

func GoRefusal(name string) string

GoRefusal is what a shim prints, and exits 2 with.

func GoShimName

func GoShimName(argv0 string) (string, bool)

GoShimName says whether argv0 names a shim (this binary run as go or gofmt, by symlink).

func GoShims

func GoShims(dir, self string) error

GoShims makes dir hold a go and a gofmt that are this binary (self) by symlink, so that run by those names it refuses (GoShimName, GoRefusal): no script, nothing to drift. Links already right are kept.

func GrokInstallLine

func GrokInstallLine(harness, session string) string

GrokInstallLine is the NOTE install prints for harness grok. Every other harness gets none. session is --session, the wake file, empty when the session chooses the path.

func GrokMonitorLine

func GrokMonitorLine(wake string) string

GrokMonitorLine is the one line the open session runs so a delivery is a turn in that window. An empty wake is the placeholder the session replaces with its own absolute path. It is a command run in the session, not a flag or a wrapper at app start.

func GrokWakePath

func GrokWakePath(home, stateDir, friend string) string

GrokWakePath is the wake file install names for a grok friend when --session names none: <state>/<friend>.wake, under the daemon's state directory, so no unit carries a path typed by hand.

func HarnessFirstError

func HarnessFirstError(out string) string

HarnessFirstError is the first line of a harness's output that says an error, one line, at most 200 bytes; "" when none does.

func HarnessSpent added in v1.2.14

func HarnessSpent(d Deliverer) float64

HarnessSpent is a harness's spend so far in USD, as the harness's own usage report says it (docs/SPEC-FRIEND.md, spend caps): DSH's record, OpenCodePriced's priced runs, or Claude's cost. 0 for a harness that reports none, which is never capped by measurement and is stopped by its provider's funds refusal instead.

func Head(s string, n int) string

Head is the first n bytes of s, with a note when it was cut.

func HeldVia

func HeldVia(friend string, ask func(ctx context.Context, argv []string) (string, error), view func(ctx context.Context) (string, error), now func() time.Time) func(context.Context) (Row, error)

HeldVia is the daemon's Held over the sprint server: friend cards <friend> (ask), its answer read by ParseHeld, which carries each card's brief. While the server refuses that verb (a server that does not serve it yet: Refused), the worker view (view, GET /api/view/worker?as=<friend>) says which work cards are on her row and the job each is delivered as, with no brief: her inbox is counted against it and a job whose work card left is retired, and a held card with no BRIEF.md is said missing and not written. The view is read once a ViewEvery (ErrNotDue between), and friend cards asked again once a ServedEvery, each refusal said in the Row's Note; with view nil a refusal is NotServed, once a ServedEvery (ErrNotDue between); an ask the server did not answer is its error.

func HostPrompt

func HostPrompt(harness, override string) (re *regexp.Regexp, src string, err error)

HostPrompt is the idle prompt pattern a hosted harness gets: override when given, else the harness's own from HostPrompts; src is its source text, as saved in the friend's state. A harness with none and no override is refused with the names there are.

func InLane

func InLane(ctx context.Context) bool

InLane says whether ctx is a lane's.

func Install

func Install(ctx context.Context, a Agent, uid int, run Launchctl, write func(path string, data []byte) error, wait func()) (path string, ran []string, err error)

Install writes the plist and loads it. A binary on /Volumes is copied under the home first (PlanBinary); a copy that cannot be made is refused and nothing is written (docs/SPEC-FRIEND.md). Then a bootout of whatever that label runs now (nothing loaded is fine), a wait until launchd no longer holds the label (WaitReleased), then a bootstrap into the user's domain, sent again after wait() while launchd answers EIO, so running it again replaces the agent with the same result. It answers the plist's path and the commands it ran.

func InstalledBinary

func InstalledBinary(home string) string

InstalledBinary is the copy of a removable-volume binary, under the home, off /Volumes (docs/SPEC-FRIEND.md).

func IsRealMessage

func IsRealMessage(subject string) bool

IsRealMessage reports whether a message subject is a real turn message (not a pong) (docs/SPEC-FRIEND.md "Check": the facts, bus).

func JobDir

func JobDir(dir, job string) string

JobDir is where a job is staged, under her working directory.

func JobText

func JobText(dir string, p Packet, sha string) string

JobText is a staged job's JOB.md: the card-contract shape (docs/SPEC-CARD-CONTRACT.md), the checkout, the branch, the outbox report and the finish.

func Known

func Known(harness string) bool

Known says whether harness is one of Harnesses.

func LaneCap

func LaneCap(tier string, caps map[string]time.Duration) time.Duration

LaneCap is the wall cap of a card of tier: the row's (caps) when it names one above zero, else DefaultLaneCaps'; a tier neither names (none was read for the card) is capped at the longest default, frontier's, so a card whose tier is unknown is never cut shorter than it could be owed.

func LaneContext

func LaneContext(ctx context.Context) context.Context

LaneContext is ctx marked as a lane's.

func LaneDirOf

func LaneDirOf(ctx context.Context, def string) string

LaneDirOf is the job directory ctx carries, else def: a harness's run in a lane goes there.

func LaneEnvironmentFault added in v1.2.13

func LaneEnvironmentFault(wall time.Duration, out, report, result, laneDir string) (string, bool)

LaneEnvironmentFault says a lane run is the friend's environment fault: it ended within InstantFail with no report file and its output names a filesystem error on a path outside the lane. It answers the harness's error line and true.

func LaneFaultParts added in v1.2.14

func LaneFaultParts(report string) (n int, line string, ok bool)

LaneFaultParts is LaneFaultReport's inverse, so a reader (nova-friend status, nova-friend lanes) renders the report's count and refusal line as the two fields the card names without re-parsing the wording in two places. ok is false when report is not a LaneFaultReport (docs/SPEC-FRIEND.md, the lanes).

func LaneFaultReport added in v1.2.14

func LaneFaultReport(n int, line string) string

LaneFaultReport is the one line a faulted lane carries on the beat: lane-fault=<n> refusal="<first line>", where n is how many identical refusals faulted it.

func LaneFaultText added in v1.2.14

func LaneFaultText(friend string, p Pronouns, fault string) (subject, body string)

LaneFaultText is the one judgment the coordinator is told when a lane faults on a provider refusal: the subject and body naming the fault, the hold verb that returns the friend's cards, and the harness check (docs/SPEC-FRIEND.md, the lanes).

func LaneFilesystemError added in v1.2.13

func LaneFilesystemError(out, laneDir string) string

LaneFilesystemError is the first line of a lane run's output that names a filesystem error (EEXIST, EACCES, EPERM, ENOENT) and names a path outside the lane dir; "" when none does. A run that trips on the shared /tmp names its path there, outside every lane.

func LaneMarkEnded

func LaneMarkEnded(who string) string

LaneMarkEnded is the mark of a finished card: "ended: card finished by <who>".

func LaneMarkRunning

func LaneMarkRunning(who string, at time.Time) string

LaneMarkRunning is the mark of a running lane: "running: <who> at <RFC3339>".

func LaneSeed

func LaneSeed(friend string, n, width int, agents, memory string) string

LaneSeed is the first turn of a lane's new session: who the friend is, from her own files, and what each later turn will be.

func LaneTakeArgv added in v1.2.7

func LaneTakeArgv(member string, n int) []string

LaneTakeArgv is lane n's ask of the sprint server: what it holds, else its next card. It names the worker's row and the lane and nothing else: no card, no epoch.

func LaneTmpDir added in v1.2.13

func LaneTmpDir(dir string) string

LaneTmpDir is the private temporary directory of the lane whose job directory is dir: <lane dir>/tmp. Wall.Exec makes it 0700 before the run and removes it after; RunWall names it at TMPDIR, TMP, TEMP and CLAUDE_CODE_TMPDIR, so two lanes get two directories and no harness run reaches the shared /tmp.

func LanguageServer

func LanguageServer(ps, username string) (pid, token string, err error)

LanguageServer finds the antigravity language server in `ps -axo user=,pid=,args=` belonging to username: its pid and CSRF token.

func LastLines

func LastLines(s string, n int) string

LastLines is the last n lines of s, without a trailing newline.

func LastResult

func LastResult(stateDir string) (at time.Time, exit int, err error)

LastResult is the newest turn's line of the daemon's log (the line a turn's end writes, "<RFC3339> lane=<n> ... exit=<n>"): when it ended and its exit. No log, or no turn in it, is the zero time.

func LimitAlikeText

func LimitAlikeText(friend string, p Pronouns, line string) (subject, body string)

LimitAlikeText is the one judgment the coordinator is told when three lanes in a row ended with the same first error line and none names a limit or a credit or quota refusal the daemon's table knows (cmd/nova-friend/limit.go, RefusalWatch): the line, so the wording is added or the cause is found, never a silent fourth retry.

func LimitDownText

func LimitDownText(friend string, p Pronouns, until time.Time, reason string) (subject, body string)

LimitDownText is what the seat is told when friend's harness hits its limit: the subject, and a body with the line that shows why and until when on her row (nova-sprint friend down --reason --until).

func LimitUnreadText

func LimitUnreadText(friend string, p Pronouns, rest time.Duration, text string) (subject, body string)

LimitUnreadText is the one judgment the coordinator is told when friend's harness refused at its limit with a text that names no reset this reads: the text, the rest she is held for, and the line that sets the true reset.

func LimitUpText

func LimitUpText(friend string, p Pronouns) (subject, body string)

LimitUpText is what the seat is told when friend's harness's reset passed.

func ListenPorts

func ListenPorts(lsof string) []string

ListenPorts reads the ports out of `lsof -Fn` (one n<host>:<port> line per socket), in the listing's order.

func Load1Of

func Load1Of(out string) (float64, bool)

Load1Of reads the one-minute load off `sysctl -n vm.loadavg` ("{ 1.23 1.50 1.60 }") or /proc/loadavg ("1.23 1.50 1.60 1/300 12345").

func LogPath

func LogPath(stateDir string) string

LogPath is the daemon's own log in the state directory: one line per delivery (launchd's own log is elsewhere: Plist).

func NewestConversation

func NewestConversation(rows string, dirs ...string) (string, error)

NewestConversation picks the first conversation of the summaries query (newest first, as `sqlite3 -json` printed it) whose workspaces hold one of dirs.

func NewestDSHSession

func NewestDSHSession(sessions, dir string) (string, error)

NewestDSHSession is the most recently modified session of dir under the sessions root: the directory names are the session ids.

func NewestWrite

func NewestWrite(fsys fs.FS, roots []string, now func() time.Time, lim ActivityLimits) time.Time

NewestWrite is the newest modification time of a file under the roots of fsys, zero when there is none (a root that is not there, an empty tree). It reads at most lim.Files files and runs at most lim.Time by now's clock, never descends into activitySkip, and reads a root named earlier once.

func NotHers

func NotHers(card, job, friend string, running func() map[string]string) string

NotHers is the refusal of a report on a card that is no longer hers: never finished, with the line naming who holds it now, as the beat's running list says (a card id or job to the friend whose lane runs it), else that no row the daemon reads names one.

func OutboxFinishArgv

func OutboxFinishArgv(friend string, c Card, verdict, head, branch, report string) []string

OutboxFinishArgv is the finish a friend's report gives her card c (its job's card, epoch and generation): LAND with a full sha Head finishes at that head with the report's first paragraph, as friend sync words it; any other verdict (HOLD, FAIL, a LAND with no full sha Head) is --failed, with a HOLD's or FAIL's full sha Head kept, and the report's first ReportChars characters. The branch is the brief's.

func PacingJudgmentText

func PacingJudgmentText(friend string, paced, width int, pacing float64, use string) (subject, body string)

PacingJudgmentText is what the coordinator is told when a friend's lanes are paced below half her row's width: one judgment.

func PacingOf

func PacingOf(setting float64) float64

PacingOf is the pacing a row's setting gives: the setting when it is a fraction in (0, 1], else DefaultPacing.

func PacingText

func PacingText(pacing float64) string

PacingText is a pacing fraction as a percent: "80%".

func ParseJob

func ParseJob(job string) (id string, epoch, gen int, ok bool)

ParseJob reads a friend's job directory name, <id>~<epoch> with .g<gen> after it from the card's second generation (nova-sprint friendJobOf); gen is 1 when it has none.

func ParseLaneCaps

func ParseLaneCaps(answer string) (caps map[string]time.Duration, ok bool)

ParseLaneCaps reads the row's lane caps off her beat's answer (row_lane_caps=flash:15m,pro:45m,heavy:1h30m,frontier:2h30m, beside row_mode and row_width); ok is false when the answer carries none, or one with a pair that is no tier:duration above zero.

func ParseMachine

func ParseMachine(answer string) (state string, ok bool)

ParseMachine reads the machine's word off a beat's answer (`machine=RUNNING` or `machine=STOPPED`); ok false when the answer carries none (a server before the word).

func ParsePong

func ParsePong(text string) (nonce string, queue, working, width int, ok bool)

ParsePong reads a pong line; ok is false when the text is no pong.

func ParseProfile

func ParseProfile(answer string) (profile string, ok bool)

ParseProfile reads the friend's wall profile off her beat's answer (row_profile=<name>, beside row_mode and row_width); ok is false when the answer carries none.

func ParseReadSlots

func ParseReadSlots(answer string) (n int, ok bool)

ParseReadSlots reads the friend's read slots off her beat's answer (row_read_slots=<n>, beside row_mode and row_width); ok is false when the answer carries none.

func ParseRow

func ParseRow(answer string) (mode string, width int, ok bool)

ParseRow reads the friend's row off her beat's answer (nova-sprint friend beat prints row_mode=<mode> row_width=<n>, as friend sync last wrote her nova-config row); ok is false when the answer carries none.

func ParseShown

func ParseShown(data []byte) (map[string]ShownEntry, error)

ParseShown parses JSON representing shown state.

func PauseBeat

func PauseBeat(marker, model string, now time.Time) (until time.Time, reason string)

PauseBeat is the friend's beat while the pause marker (its line, ReadPause) stands: down until PauseBeatAhead from now, with the provider's exact message as the reason. `friend beat <friend> --until <t> --reason <r>` is a worker's verb the sprint server serves; while her last beat says so she is down and the dealer deals her nothing (docs/SPEC-SPRINT.md).

func PermissionRejection

func PermissionRejection(out string) string

PermissionRejection is the first line of out that says a permission was refused, one line, at most 200 bytes; empty when none does.

func PlanBinary

func PlanBinary(binary, home string) (path string, copy bool, err error)

PlanBinary is the path the plist will name. A binary on /Volumes is copied to InstalledBinary before the plist is written (copy=true); a binary off /Volumes is used as-is (copy=false). A home that is empty or itself on /Volumes is refused, because a copy there would stay on the wall (docs/SPEC-FRIEND.md).

func PlatformPermitted

func PlatformPermitted(context.Context, Exec) (bool, error)

PlatformPermitted is false where the platform cannot hold the permission. It does not call run and it does not ask. The window step then refuses with the same remedy as a missing grant (docs/SPEC-FRIEND.md, Reach).

func PlistArgs

func PlistArgs(plist string) []string

PlistArgs is the ProgramArguments array of a launchd plist, in order; nil when the plist has none or cannot be read as XML.

func PlistDriftLine

func PlistDriftLine(plist string, running []string) string

PlistDriftLine is the line a daemon says on start when its own arguments (running, after the program's name) differ from the installed plist's: a launchctl kickstart restarts the agent launchd loaded, with the arguments it read then, so an edit to the plist is lost until it is booted out and bootstrapped again (the finding of 2026-10-05). The daemon's part of each is compared, from its verb "run" on (the secrets wrap and the binary's path are the plist's own). Empty when there is no plist, it names no run, or the two agree.

func PongLine

func PongLine(nonce string, queue, working, width int) string

PongLine is a session's pong as it travels on the bus: pong <nonce> queue=<n> working=<n> width=<n>. A session sends it to answer a reach step or a delivery check (nova-friend pong); liveness is the nova-hook record's alone, never a pong.

func PongRequest

func PongRequest(text string) (kind, nonce string, only bool)

PongRequest reads a delivery's text for the pong it asks for: its kind (PongCheck for a session check, else PongWake), the nonce of its pong command, and whether it asks for nothing else (a session check, a wake turn, an idle wake, which a newer request of its kind supersedes). A delivery carrying a bus message is never only a request.

func ProgressArgv

func ProgressArgv(friend string, cards []Card) [][]string

ProgressArgv is the sprint server's verbs that stamp progress on the cards, one for the cards of each epoch, in the order of the epochs: `progress --as friend.<friend> <card>... --epoch <n>`, her row as the holder, as FinishArgv names it. Only the holder's stamp is taken: the server refuses one for a card she does not work, and one sent as her bare name (held by friend.<name>, not <name>).

func PromptPattern

func PromptPattern(s string) *regexp.Regexp

PromptPattern compiles a prompt pattern, panicking on a bad constant.

func PronounList added in v1.2.16

func PronounList() string

PronounList is the three sets a row may name, for a flag's help and refusal.

func ProviderLimit

func ProviderLimit(session, out string) error

ProviderLimit reads the tail of a lane turn's output (the last LimitTail bytes: a harness says it last) for a rate limit or out of funds, out of funds first: OutOfFunds, RateLimited, or nil. A line with its reset beside it ("Insufficient AI Credits ... will refresh 6:52 PM") is the harness's own limit (Limits: down until the reset, then woken), never out of funds here.

func ProviderRefusal

func ProviderRefusal(out string) (reason string, ok bool)

ProviderRefusal reads a failed turn's output for a provider's refusal: the error type and the head of its message, one line; ok is false when the output carries none, or only a transient one.

func PublishCost

func PublishCost(outbox string, t LaneTokens, rp RoutePrice, model string) error

PublishCost writes the card's cost into its outbox: the Cost: line into REPORT.md, which friend sync reads, and the tokens: and cost: lines onto RESULT.md when it is there. A report not there is not made.

func PushedHead

func PushedHead(dir string, c Card) (head, branch string)

PushedHead is the head the friend pushed for card c: the branch its brief's STATUS line names, read as origin's remote-tracking ref in a clone under dir's jobs/<job>/ (a push writes it; no network is asked). Empty when the brief names no branch or no clone holds the ref; branch is the brief's either way.

func Pushing

func Pushing() []string

Pushing is the harnesses whose adapter has a deliver command, in the order of Harnesses.

func QuoteWhy

func QuoteWhy(s string) string

QuoteWhy quotes a reason string if it contains whitespace or quotes.

func RateJudgmentText

func RateJudgmentText(friend string, p Pronouns, cap, width int, reason string) (subject, body string)

RateJudgmentText is what the coordinator is told when a friend's lane cap was lowered RateJudgeAfter times within RateJudgeWithin: one judgment.

func Read

func Read(readJSON []byte, id string) bool

Read says whether read.json (a map of message id to true) marks id read.

func ReadBeginArgv

func ReadBeginArgv(friend string, r AskedRead) []string

ReadBeginArgv begins an asked read.

func ReadLaneFault added in v1.2.14

func ReadLaneFault(stateDir string) string

ReadLaneFault is the lane fault report, or "" when there is none.

func ReadPause

func ReadPause(stateDir string) string

ReadPause is the pause marker's line; "" when the lanes are not paused.

func ReadPrompt

func ReadPrompt(friend, jobDir string) string

ReadPrompt is the one turn a read runs: do READ.md, stop when RESULT.md is written.

func ReadQueue

func ReadQueue(dir string) (queue, working int, err error)

ReadQueue is the queue file's counts; a file that is not there counts zero, and a file that is no queue is an error the caller shows.

func ReadQueueArgv

func ReadQueueArgv(friend string) []string

ReadQueueArgv is the sprint verb that is the reader's beat and answers its queue.

func ReadReturnArgv

func ReadReturnArgv(friend string, r AskedRead, reason, usage string) []string

ReadReturnArgv returns a read that has no verdict, with why.

func ReadText

func ReadText(friend, jobDir string, r AskedRead) string

ReadText is READ.md: the read's job, as the reader loops wrote it.

func ReadVerdict

func ReadVerdict(result string) (verdict, finding string)

ReadVerdict reads a read's RESULT.md: its verdict (ok or broken; "" when there is none), and the finding recorded with it, the report line then the body.

func ReadVerdictArgv

func ReadVerdictArgv(friend string, r AskedRead, verdict, finding, usage string) []string

ReadVerdictArgv records a read's verdict (ok or broken) with its finding and usage.

func ReaderOf

func ReaderOf(friend string) string

ReaderOf is the name of a friend's reader row.

func RealExec

func RealExec(ctx context.Context, dir, name string, args []string, stdin string) (string, int, error)

RealExec runs the command through os/exec: the program directly, never a shell, so a message's text is never interpolated. No clock bounds it: a turn that prints keeps running however long it takes, and the daemon stops one silent past its SilentStop by cancelling ctx (the finding of 2026-10-04: a fixed ten-minute cap killed real work mid-turn). Every write to stdout or stderr is said to the watch in ctx (WithOutputSeen). The command is its own session leader (Setsid), and on a cancel the whole group is signalled, SIGTERM then SIGKILL after KillDelay: a harness that forks (opencode run does) leaves no orphan behind a stop. On a nonzero exit the output carries the head of stderr after stdout: a harness says why it refused there (dsh does).

func Record

func Record(stateDir, line string) error

Record appends one line to the daemon's log; a log that cannot be written is not a reason to stop delivering, so the error is answered for the status file and nothing else.

func ReportPara added in v1.3.16

func ReportPara(report string) string

ReportPara is a report's first line that is no Verdict or Head line and no heading, on one line.

func ReportVerdict added in v1.3.16

func ReportVerdict(report string) (verdict, head string)

ReportVerdict is a report's verdict (the first word of its first Verdict: line, upper case, with markdown and punctuation trimmed; "" for none) and its head (the first word of its first Head: line, lower case), read as friend sync reads them.

func RescueStray

func RescueStray(job LaneJob) []string

RescueStray moves a report a run wrote under a relative spelling of the outbox (the job directory joined with the outbox path less its leading slash: the stray tree of 2026-10-07) into the outbox, REPORT.md and RESULT.md each when the outbox lacks it; it answers the files moved.

func ResentLine

func ResentLine(d AntigravityDelivery) string

ResentLine heads a delivery sent again into the conversation that reads.

func ReworkedBrief

func ReworkedBrief(brief string) string

ReworkedBrief is a reworked card's brief with its fix first: STATUS; THE ONE THING LEFT, the fix; The reader found, the finding (or why the attempt exists when no reader found anything); the carried head and the branch it is carried onto; how the report is checked; then the rest of the prelude as the server wrote it, and the card with its STOP the fix alone. A brief that asks no fix, and one already reworked, are answered as they are.

func RowConfigDir

func RowConfigDir(answer string) string

RowConfigDir reads the friend row's config_dir off her beat's answer (row_config_dir=<dir>, the directory her claude lanes run with as CLAUDE_CONFIG_DIR); empty when the answer carries none.

func RunWall

func RunWall(args, env []string, stdin io.Reader, stdout, stderr io.Writer) int

RunWall is the wall verb: args are its flags, "--", and the command; env is the environment the command gets inside the wall (its HOME is the one the deny list is under). It prints nothing on stdout but the command's own, so a harness's answer is read through it as it is; a refusal is one WALL REFUSED line per problem on stderr and sandbox.ExitRefused. The answer is the command's exit.

func RunnerEnded

func RunnerEnded(log, job string) (end string, dead bool)

RunnerEnded reads a runner's log for the job: dead is true when the job's last event is an END whose last word is report=no and no LIMIT came after the START before it; end is that END line.

func RunnerLog

func RunnerLog(dir string) string

RunnerLog is the log of the runner beside her working directory dir, its last RunnerLogCap bytes: dir's runner.log, else the one in the directory her working directory (its links resolved) is in; "" when there is none.

func RunsCards

func RunsCards(harness string) bool

RunsCards says harness runs each card as a process of its own (a CardRunner) when its row is one-shot: it has no session to push a turn into, so install and run owe it no deliver-command refusal. The session's nova-hook owns delivery and proof independently of these lane processes. NewDeliverer answers its batch adapter, the wake file (ClaudeWake); NewClaude is the one that runs cards.

func SessionCheckText

func SessionCheckText(nonce, pong, to string) string

SessionCheckText is the delivery check as the session reads it: the nonce, and the one line to run with nothing to fill in, sent to to (the seat, else the coordinator; the pong verb's own default when empty).

func SessionCost

func SessionCost(export string) (float64, error)

SessionCost is the cost of a session from its export: the sum of its assistant messages' costs. The export may be preceded by a line of its own (opencode says what it exports on stderr); the JSON starts at the first '{'.

func SessionMissing added in v1.2.7

func SessionMissing(firstError string) bool

SessionMissing says a turn's first error line says the session is gone.

func SnapEpoch added in v1.2.7

func SnapEpoch(dir string, row Row, keep map[string]bool, asked, now time.Time, record func(string)) (int, error)

SnapEpoch moves every older-epoch inbox and outbox item under dir to the retired folder named for its epoch, at the sprint's epoch now (Row.Epoch, her row's answer): every inbox and outbox job whose name parses to an epoch older than it (ParseJob), that no lane runs (keep) and that no card on her row holds (row.Cards), and whose directory stood before the ask began (asked, as the inbox's retire guards friend sync's write), is moved whole, so finished work is kept, never deleted, and the live folders start the new epoch empty. A job whose name parses to no epoch, and one that is no single path element, is never moved (a job any other hand put there: docs/SPEC-FRIEND.md, the inbox). record gets one line per move; the first error is answered, the rest still moved. It answers how many moved. Zero is a sprint epoch of no card's naming: nothing is retired on it.

func SpendCapJudgmentText added in v1.2.14

func SpendCapJudgmentText(friend string, p Pronouns, provider string, spent, cap float64) (subject, body string)

SpendCapJudgmentText is what the seat and the owner are told when a friend's spend has passed its cap: one judgment naming the friend, the provider and the spend.

func StageJudgmentText

func StageJudgmentText(friend string, n *NotStageable) (subject, body string)

StageJudgmentText is what the coordinator is told of a stage that waits on a person: one judgment, with its remedy.

func Staged

func Staged(dir, job string) bool

Staged says a job's JOB.md is there.

func StateDirIn

func StateDirIn(dir string) string

StateDirIn is the daemon's state directory under the friend's working directory, <dir>/.nova-friend: inside the directory her session may write, so a sandboxed session's pong lands where the daemon reads it (the finding of 2026-10-05).

func StopReturnArgv

func StopReturnArgv(row, card string, gen int, epoch string, lane int) []string

StopReturnArgv is the store's verb for one stop-return, as the wire of 2026-10-08 says it.

func TakeArgv

func TakeArgv(friend, card, reason string) []string

TakeArgv is the coordinator's verb that takes a dealt, unstarted card back from the friend for the dealer: `friend take <friend> <card> --reason <why>`. The sprint server does not serve it to a friend's daemon (it is the coordinator's class, and the server runs only the workers' verbs), so the lanes never send it: they ask the coordinator (TakeBackNote).

func TakeBackNote

func TakeBackNote(friend, card, job, why string) (subject, body string)

TakeBackNote is the bus note that asks the coordinator to take a card back for the dealer: the subject, and a body naming the card, why, and the exact verb to run (TakeArgv).

func TmuxFor

func TmuxFor(d Deliverer, name, stateDir string) error

TmuxFor points a Tmux deliverer at the friend's hosted session: the session and prompt host saved in stateDir, else friend-<name> and the generic prompt. Any other deliverer is left alone.

func TmuxSession

func TmuxSession(name string) string

TmuxSession is the tmux session that hosts the friend name.

func TokenCapOf

func TokenCapOf(answer string) (int64, bool)

TokenCapOf reads the row's token cap off her beat's answer (row_token_cap=<n>, beside row_mode and row_width; 0 is no cap); ok is false when the answer carries none, or one that is no whole number at or above zero.

func TokenCapReport

func TokenCapReport(friend string, c int64, tokens int64, turns int, last string) string

TokenCapReport is the REPORT.md of a card whose lane was stopped at the per-card token cap: HOLD with no head, one paragraph naming the cap, the tokens and turns it had spent and the last step; the card goes to a bud and whatever was pushed is a draft only.

func TokensSQL

func TokensSQL(session string) (string, error)

TokensSQL is the query for the tokens of a session and its children from opencode's database.

func TypeScript

func TypeScript(bundle, text string) string

TypeScript is the osascript that brings bundle frontmost, types text and submits it with Return (key code 36). It never asks for permission.

func TypedLine

func TypedLine(text string) string

TypedLine is text as the one line a TUI is typed: each newline shown as " ⏎ ", as the Grok adapter does (SPEC-FRIEND.md, "Hosted in tmux").

func UnaddressedLand

func UnaddressedLand(by, report, brief string) (held string)

UnaddressedLand is the one fix check of a LAND, the daemon's outbox pass's and friend sync's: held is UnaddressedText when report does not address the fix the brief asks (BriefFix, in either form), "" when it does or when brief asks none. A held LAND is finished as a HOLD, its head kept.

func UnaddressedText

func UnaddressedText(by, fix string, missing []string) string

UnaddressedText is the first words of the finish the holder by (the daemon, or friend sync) sends for a LAND whose report does not address THE ONE THING LEFT.

func Undriven

func Undriven(d Deliverer, harness string) (why, remedy string, ok bool)

Undriven says nova-friend can push nothing into a session of harness through d: a Stub, with why and the remedy (AdapterRemedy); ok is false for an adapter with a deliver command.

func Uninstall

func Uninstall(ctx context.Context, a Agent, uid int, run Launchctl, remove func(path string) error) (ran []string, err error)

Uninstall boots the agent out and removes its plist; an agent that is not there is fine.

func WaitReleased

func WaitReleased(ctx context.Context, run Launchctl, target string, timeout, poll time.Duration, sleep func(time.Duration)) (time.Duration, error)

WaitReleased waits until launchd no longer holds the service target (gui/<uid>/<label>) after its bootout: `launchctl print <target>` asked every poll, until it no longer finds the service (anything but exit 0 with the service's own `<target> = {` block), at most timeout. A bootstrap sent while launchd is still removing the old service is refused with "37: Operation already in progress" for as long as the old daemon takes to exit (about 5 s, the seat's adopt runs of 2026-10-07), more than BootstrapTries one second apart. It answers how long it waited, and on timeout an error naming the label and the seconds.

func WakeLine

func WakeLine(text string) string

WakeLine is text as the one line a monitor event is: the monitor makes an event per line, and a flood of lines is how the harness stops a monitor (its guide's "Volume Control"), so a newline in the text becomes " ⏎ ".

func WakeOf

func WakeOf(active, listing, dir, wake string) (string, error)

WakeOf is the wake file a delivery goes to: the file a `tail` tails under the grok session open in dir, from the harness's active_sessions.json (pid and cwd per open window) and the process listing (`ps -axww -o pid=,ppid=,args=`). A named wake must be that file. A session whose pid is not in the listing is a stale record, not a session. No window in dir is ErrNoSession.

func WatchPath

func WatchPath(stateDir string) string

WatchPath is the cursor file in the state directory.

func WithCost

func WithCost(report, line string) string

WithCost is report with line under its Head: line (at the end when it has none); a report that already carries a Cost: line is returned as it is.

func WithLaneDir

func WithLaneDir(ctx context.Context, dir string) context.Context

WithLaneDir is ctx carrying dir, the job directory the lane's harness runs in (LaneJob.Dir).

func WithOutputSeen

func WithOutputSeen(ctx context.Context, seen func()) context.Context

WithOutputSeen is ctx carrying seen, called each time the command a delivery runs prints to stdout or stderr: a turn that prints is working, and only a turn silent past the daemon's SilentStop is stopped.

func WithOutputTail

func WithOutputTail(ctx context.Context, tail func([]byte)) context.Context

WithOutputTail is ctx carrying tail, handed each write the command a delivery runs prints to stdout or stderr.

func WorkspaceConversations

func WorkspaceConversations(rows string, dirs ...string) ([]string, error)

WorkspaceConversations is every conversation of the summaries query, in its order (newest first), whose workspaces hold one of dirs.

func WriteHost

func WriteHost(stateDir string, h Hosted) error

WriteHost saves the host state of the friend whose state directory this is.

func WriteLaneFault added in v1.2.14

func WriteLaneFault(stateDir, report string) error

WriteLaneFault records a lane's provider-refusal fault report; an empty report removes the file (a clear, or a stale file a restart cleared).

func WritePause

func WritePause(stateDir, message string, at time.Time) error

WritePause records the exact provider message that held the friend down.

func WriteStatus

func WriteStatus(stateDir string, s Status) error

WriteStatus is the daemon's write of its state.

func WriteWatch

func WriteWatch(stateDir string, w Watch) error

WriteWatch saves the cursor atomically (write a temporary file, rename it over the old), so a run killed in the middle leaves the old cursor whole.

Types

type ActivityLimits

type ActivityLimits struct {
	Files int
	Time  time.Duration
}

ActivityLimits bounds one walk: the files it reads the time of (a directory costs time, not files) and the time it may take on the clock it is given. A walk that reaches either answers with the newest write it has read.

type Agent

type Agent struct {
	Friend, Harness, Dir, Session string
	Adapter, DeliveryDir          string   // explicit Codex folder route; session remains the real harness session
	StateDir                      string   // the daemon's state files, when not the default under Home
	Binary                        string   // this tool, by absolute path
	Copy                          CopyFile // places a removable-volume binary under Home; nil refuses it
	Redis, Server                 string   // the bus store and the sprint server
	Home, Path                    string   // the environment the agent runs in
	LaunchdLog                    string   // launchd's own stdout and stderr path, off the friend's volume
	// Secrets are the names of the secrets the daemon needs in its environment
	// (never values); with any, the command is wrapped in nova-secrets exec as
	// the seat Seat, with SecretsTool and Sops by absolute path, the store under
	// Home/nova-bench/secrets and the key under Home/.config/nova-secrets.
	Secrets                 []string
	Seat, SecretsTool, Sops string
	// Coordinator, SilentStop and BrokenAfter are the daemon's flags of the
	// same names, written only when set and not the default.
	Coordinator string
	SilentStop  time.Duration
	BrokenAfter int
	// ConfigDir is the friend's harness config directory (CLAUDE_CONFIG_DIR),
	// the daemon's --config-dir, written only when set.
	ConfigDir string
	// Pronouns are the friend's pronouns (the row's pronouns setting), the daemon's --pronouns,
	// written only when set.
	Pronouns string
	// DenySelf is the coordinator's self the daemon's lane wall never writes inside, the
	// daemon's --deny-self, written only when set (the daemon's default, DefaultDenySelf,
	// stands when it is not).
	DenySelf string
	// WallReads is what the harness reads inside the lane's wall beyond the system roots and its
	// own directory (a pinned harness shim under the home), the daemon's --wall-reads, written only
	// when set.
	WallReads string
	// Command, when set, is what the agent runs in place of the daemon: this tool's
	// own verb and flags, after Binary.
	Command []string
	// Sleep is how Install waits between two looks at the old service after the
	// bootout (ReleasePoll); nil is time.Sleep. A test passes its own clock.
	Sleep func(time.Duration)
}

Agent is one friend's launchd agent: the daemon as launchd runs it, never started by the model (SPEC-FRIEND.md, the daemon).

func (Agent) Args

func (a Agent) Args() []string

Args is the daemon's command line: with Secrets, nova-secrets exec opens exactly those names for the daemon (--only) and refuses to start it without every one (--require), then the daemon itself after the --.

func (Agent) BinaryPlan

func (a Agent) BinaryPlan() (path string, copy bool, err error)

BinaryPlan is where the agent's binary is placed, and whether it is copied there (PlanBinary).

func (Agent) Label

func (a Agent) Label() string

Label is the agent's launchd label.

func (Agent) Plist

func (a Agent) Plist() string

Plist is the agent's plist: RunAtLoad and KeepAlive, so it starts at login and is restarted when it dies (pending messages redeliver first, nova-bus's rule). launchd opens its own log itself, before the daemon runs, and cannot open one on a network volume (EX_CONFIG, measured 2026-10-03), so that log is LaunchdLog, under the home directory, and so are the daemon's state files and record (DefaultStateDir).

func (Agent) PlistPath

func (a Agent) PlistPath() string

PlistPath is where the agent's plist lives under home.

func (Agent) Said

func (a Agent) Said() string

Said is the command line as a plan says it, with no path in it: the secrets wrap by its names and seat, then the daemon's own flags, --redis and --server left to the install line that gave them.

type AgentAPIRefusal

type AgentAPIRefusal string

AgentAPIRefusal is the error agentapi printed in its JSON, at exit 0: the harness said no (a wrong conversation, a missing token).

func (AgentAPIRefusal) Error

func (r AgentAPIRefusal) Error() string

type Antigravity

type Antigravity struct {
	Dir, Session string
	Run          Exec
	Out          io.Writer                  // the daemon's record, when set
	Home         string                     // the user's home ($HOME when empty): app data under Home/.gemini/antigravity
	User         string                     // daemon's username (current process user when empty)
	FS           fs.FS                      // rooted at Home (os.DirFS(Home) when nil): the mailbox is read through it
	Wait         func(context.Context) bool // one poll interval; false once ctx has ended (real time when nil)
	Now          func() time.Time           // the daemon's clock, every time in the ledger (time.Now when nil)
	State        string                     // the daemon's state directory: the ledger's file (AntigravityLedgerFile); "" keeps it in memory
	// contains filtered or unexported fields
}

Antigravity delivers through the harness's own agent-message channel: `agentapi send-message --title=nova-friend <conversation> <text>`, a client of the running language server (the wrapper under ~/.gemini/antigravity/bin, which execs the app's language_server). The server writes the text into the conversation's mailbox (~/.gemini/antigravity/brain/<conversation>/.system_generated/messages/) as a high-priority message and its watcher starts a turn on it; the session marks the message read in read.json there as it takes it. Measured 2026-10-04 08:52 ET: sent at :28, the turn's first step at :32, the friend's "got it" on nova-bus2 at :35.

The server's address and CSRF token are read off the language_server process each delivery (ps, then lsof for its listening ports; the one that answers get-conversation-metadata is the plaintext one). The conversation is the live one (Follow), else the one named by Session, else the newest root conversation whose workspace is Dir, from the harness's conversation_summaries.db (read immutable, through sqlite3).

The mailbox queues: a turn the message starts runs on, and a second message waits in the mailbox for it, the harness's own order for its agents. So Deliver answers 0 once agentapi has taken the message into the mailbox, and never waits for the session to read it (the finding of 2026-10-05 and 06: a delivery that waited two minutes for the read held every other message and the session check behind it while the friend worked through a long turn, and three such waits gave a message up that was already in her mailbox). Every delivery is kept in the ledger (antigravity_ledger.go): who read it, and what to send again if its conversation stops reading. agentapi exits 0 on an error too (a wrong conversation, a missing token): the JSON it prints is the truth, never its exit code. What the harness refuses (no language server, no token, no conversation, no port that answers for it, no mailbox), and a session that has read nothing delivered for the check period, is a SessionRefused naming why, never a Deferred: the daemon marks the session broken with the reason, said once, and keeps every message pending on the bus.

func (*Antigravity) Deliver

func (a *Antigravity) Deliver(ctx context.Context, text string) (int, error)

Deliver: find the server and the conversation; refuse while the conversation has read nothing delivered for the check period (the session is down: the message stays pending on the bus); send; and answer 0 once agentapi has taken the message into the mailbox, kept in the ledger.

func (*Antigravity) Follow

func (a *Antigravity) Follow(ctx context.Context, now time.Time)

Follow reads, at most every AntigravityFollowEvery, who has read the deliveries (observe), and moves delivery to the conversation that reads: when the live conversation has left AntigravityStopped deliveries unread past AntigravityReadBound and read nothing since the oldest of them, a conversation the daemon delivered to that read one of those deliveries since then is the live one (the one that read last), said once ("antigravity: live conversation is now <id> (the named one stopped reading)"), and never moved again within AntigravitySwitchHold. Every delivery the old conversation left unread is sent again into the new one, headed by ResentLine; one whose send fails is sent again at the next look.

func (*Antigravity) Live

func (a *Antigravity) Live() string

Live is the conversation deliveries go to: the one delivery was moved to, else the one the last delivery went to, else the one named by Session; "" before the first delivery when none is named.

type AntigravityDelivery

type AntigravityDelivery struct {
	ID           string    `json:"id"`
	Conversation string    `json:"conversation"`
	DeliveredAt  time.Time `json:"delivered_at"`
	ReadAt       time.Time `json:"read_at,omitzero"`
	ResentTo     string    `json:"resent_to,omitempty"`
	Text         string    `json:"text,omitempty"`
}

AntigravityDelivery is one delivery: its message id in the conversation's mailbox ("" until it lands), when it went in and when the daemon saw it read, the conversation it was sent again into, and its text while it may be sent again.

type AntigravityLedger

type AntigravityLedger struct {
	Followed   string                `json:"followed,omitempty"`
	From       string                `json:"from,omitempty"`
	Since      time.Time             `json:"since,omitzero"`
	Deliveries []AntigravityDelivery `json:"deliveries"`
}

AntigravityLedger is the ledger's file: the conversation delivery follows (Followed, moved to while the named session was From, at Since) and every delivery, oldest first.

type AskedRead

type AskedRead struct {
	ID     string
	Epoch  string
	Gen    int // the read card's generation as the queue carries it (0: a queue before it); a stop-return names it
	Packet ReadPacket
	Col    string // its column on the reader queue (asked, reading)
	Lane   int    // the read lane the server says holds it (take --as reader-<friend> --lane <n>); 0 none
}

AskedRead is one card of the reader queue in the asked column.

func ParseReadQueue

func ParseReadQueue(out string) ([]AskedRead, error)

ParseReadQueue reads `queue --as reader-<friend> --json`: the asked reads, in the queue's order, each with the queue's epoch.

func ParseReadQueueAll added in v1.2.7

func ParseReadQueueAll(out string) ([]AskedRead, error)

ParseReadQueueAll is every read on the reader queue, whatever its column, each with the queue's epoch: a read lane that holds a read begun before it finds its packet here.

type BeatWords

type BeatWords struct {
	StopReturns int
}

BeatWords are what a beat carries beside the friend's name: how many stop-returns the lanes owe (stop.go, OwedStopReturns), said while it is above zero (friend beat --stop-returns).

type BusFacts

type BusFacts struct {
	Friend    string `json:"friend"`
	RealSince int    `json:"real_since"`
	LastReal  string `json:"last_real"` // RFC3339 or "-"
}

BusFacts carries facts about real messages on the bus.

func (BusFacts) Line

func (bf BusFacts) Line() string

Line renders the CHECK BUS line.

type Card

type Card struct {
	ID     string `json:"id"`
	Brief  string `json:"brief"`
	Outbox string `json:"outbox"`
	// Lane, ServerEpoch and ServerGen are what the server's answer to a lane's ask named
	// (lane_take.go): the lane that holds the card, the epoch and the generation it holds it
	// at. A card a lane holds is reported by these, never by its directory's name.
	Lane        int    `json:"lane,omitempty"`
	ServerEpoch string `json:"server_epoch,omitempty"`
	ServerGen   int    `json:"server_gen,omitempty"`
}

Card is one card a lane hands: its id (the queue file's), its brief, and the outbox directory its REPORT.md and RESULT.md go to.

func NextCard

func NextCard(dir string, skip func(Card) bool) (c Card, found bool, err error)

NextCard is the first card of dir's queue file (inbox/QUEUE.json, in its order) that is queued, delivered (inbox/<id>~<epoch>/BRIEF.md), not done (no outbox/<id>~<epoch>/RESULT.md, and no REPORT.md: a card with a report is friend sync's to finish) and not skipped (held by another lane, or set aside); found is false when there is none.

func (Card) Epoch

func (c Card) Epoch() string

Epoch is the sprint epoch alone, without the job's generation (docs/FRIENDS.md): the server's, when its answer named one, else the job directory's.

func (Card) Gen

func (c Card) Gen() int

Gen is the card's generation, its directory's .g<gen> (friend sync names a card dealt again to the same friend <id>~<epoch>.g<gen>); 1 when it has none.

func (Card) Report

func (c Card) Report() string

Report is the card's REPORT.md, the one friend sync finishes the card from.

func (Card) Result

func (c Card) Result() string

Result is the card's RESULT.md, whose presence after a turn is the card done.

type CardRunner

type CardRunner interface {
	Deliverer
	// RunCard runs card c to its end: its exit, and an error naming what the
	// outbox lacks when the run wrote no REPORT.md or RESULT.md there.
	RunCard(ctx context.Context, c Card) (LaneTurn, error)
	// Refusal is why no card can run now, with its remedy; empty when one can.
	Refusal() string
}

CardRunner is a Deliverer whose one-shot lane runs each card as a process of its own: no session to open or keep, the card's brief the whole prompt, no bus message riding along, and the card's result read from its outbox, never from the process's output (docs/SPEC-FRIEND.md, one-shot lanes; the owner, 2026-10-04: four Claude accounts as heavy-tier friends). Claude is one.

type CardUsage

type CardUsage interface {
	Tokens(ctx context.Context) (Tokens, error)
}

CardUsage is a running card's tokens so far, read from its harness's own record. A test hands a fake; a lane hands the harness's stream or export.

type CheckReport

type CheckReport struct {
	Friends []FriendCheck `json:"friends"`
	Summary CheckSummary  `json:"summary"`
}

CheckReport is the top-level report for JSON serialization.

func (CheckReport) Lines

func (cr CheckReport) Lines() []string

Lines renders all output lines for the full report.

type CheckResult

type CheckResult struct {
	Harness string        `json:"harness"`
	Nonce   string        `json:"nonce"`
	Stage   string        `json:"stage,omitempty"`
	Why     string        `json:"why,omitempty"`
	Took    time.Duration `json:"took"`
}

CheckResult is one run of the check: Stage empty is a pass.

func PushProof

func PushProof(ctx context.Context, c Conformance) (res CheckResult, remedy string, undriven bool)

PushProof runs the check c once as the push proof: an undriven harness is refused before anything is delivered; otherwise the round trip runs, and a failure whose delivery the adapter answered with a Deferred carrying a Remedy (a session it cannot drive) answers that remedy with undriven set. remedy is empty for any other failure: the caller names its own.

func (CheckResult) Line

func (r CheckResult) Line() string

Line is the result as nova-friend check prints it.

type CheckSeams

type CheckSeams struct {
	Now         func() time.Time
	Home        string
	Launchctl   Launchctl
	ReadStatus  func(friend string) (Status, bool, error)
	ReadHook    func(friend string) (HookRecord, error) // her nova-hook record; nil reads none
	ReadLog     func(friend string) ([]string, error)
	BusLog      func(ctx context.Context, friend string, since time.Time) (realCount int, lastReal time.Time, err error)
	ReadWork    func(friend string, dir string) (inboxCount, outboxCount int, newestName string, newestAt time.Time, err error)
	ListFriends func() ([]string, error)
	HarnessDir  func(friend string) (harness, dir string, err error)
}

CheckSeams provides external dependencies for friend check facts.

type CheckSummary

type CheckSummary struct {
	Friends int `json:"friends"`
	OK      int `json:"ok"`
	Broken  int `json:"broken"`
	Down    int `json:"down"`
	Untrue  int `json:"untrue"`
}

CheckSummary is the counts across all checked friends.

func ComputeSummary

func ComputeSummary(checks []FriendCheck) CheckSummary

ComputeSummary aggregates verdicts across friends (docs/SPEC-FRIEND.md "Check": the summary).

func (CheckSummary) Line

func (s CheckSummary) Line() string

Line renders the CHECK OK summary line.

type Claude

type Claude struct {
	Stub
	Friend, Dir string
	// ConfigDir is the friend row's config_dir as the daemon last read it;
	// nil or empty refuses every card.
	ConfigDir func() string
	Run       Exec
	Program   string           // "claude" when empty
	Out       io.Writer        // where the run's output goes, when set: the daemon's record
	Now       func() time.Time // time.Now when nil: the clock a limit's reset is read against
	// TokenCap is the friend row's per-card token cap as the daemon last read
	// it (TokenCapOf; 0 none); nil is DefaultTokenCap (tokencap.go).
	TokenCap func() int64
	// contains filtered or unexported fields
}

Claude is the claude harness: no deliver command in batch (Stub, passive: the daemon reads nothing for it), and in one-shot mode each card run as `env CLAUDE_CONFIG_DIR=<dir> claude -p <brief> --output-format stream-json --verbose` and the trim (ClaudeTrim) in Dir with stdin from /dev/null, priced and its limit read from its stream-json (adapter_claude.go). The config directory is the friend row's config_dir, so each friend is its own account's login and settings (its permission mode among them); stream-json prints as the run works, so the daemon's silence watch sees a working run.

func NewClaude

func NewClaude(friend, dir string, run Exec, out io.Writer) *Claude

NewClaude is the claude harness's card runner over run, in the friend's directory dir, its runs' output going to out when set.

func (*Claude) Refusal

func (c *Claude) Refusal() string

Refusal names the missing config_dir and its remedy: a claude friend in one-shot mode runs only as her own account.

func (*Claude) RunCard

func (c *Claude) RunCard(ctx context.Context, card Card) (LaneTurn, error)

func (*Claude) RunRead

func (c *Claude) RunRead(ctx context.Context, model, prompt string) (LaneTurn, error)

RunRead is one read of the friend's reader row (ReadHarness; docs/SPEC-FRIEND.md, the reader row): a card run's call with the prompt for the brief and the model of the read's tier ("" is the account's own) before the trim, as her own account, priced and its limit read as a card's run is.

func (*Claude) SpendLine

func (c *Claude) SpendLine() string

SpendLine is every run's cost so far and the five-hour and weekly windows the last run read: `spend: harness=claude runs=<n> cost_usd=<sum> five_hour=<f> seven_day=<f> five_hour_resets=<t> seven_day_resets=<t>`.

func (*Claude) Spent

func (c *Claude) Spent() (cost float64, usage Usage)

Spent is the cost of every run so far, in US dollars, and the last usage a run measured (zero: none yet): what the daemon says on its record.

type ClaudeWake

type ClaudeWake struct {
	Dir, Name string
	Now       func() time.Time // time.Now when nil
	Out       io.Writer        // the daemon's record, when set
}

ClaudeWake is the claude adapter: Claude Code has no command that puts a turn into a running session from outside, so Deliver puts nothing in. It appends one line per push to the wake file in Dir, the friend's state directory (ClaudeWakePath(Dir, Name)): the clock, then the pushed text on one line, which carries the nonce or message id and the path of what was pushed. The session's own wait (ClaudeWaitLine), running as a background task, returns when the file grows, and its exit re-invokes the session. The file is made when absent, synced, and never truncated; a missing Dir is a refusal naming it, and nothing is made. Name is the session's name for the file, else the friend of Dir's status file. It is Passive: the daemon takes nothing off the stream for claude.

func (*ClaudeWake) Deliver

func (c *ClaudeWake) Deliver(_ context.Context, text string) (int, error)

func (*ClaudeWake) Passive

func (*ClaudeWake) Passive()

Passive marks the claude adapter: the session's own wait reads the stream.

type Conformance

type Conformance struct {
	Friend, Harness string
	Deliver         Deliverer
	Store           bus.Store
	Within          time.Duration
	Now             func() time.Time
	Wait            func(ctx context.Context) bool // one CheckPoll; false once ctx has ended
	Nonce           func() string
	Text            func(nonce string) string // the session check as the session reads it (SessionCheckText)
}

Conformance is the one promise every harness adapter makes, checked end to end (docs/SPEC-FRIEND.md, delivery-conformance-r.w1; the presence model's Ask then Answer within the bound, tla/FriendPresence.tla): a session check carrying a fresh nonce goes in through the adapter, the session runs the exact pong line it carries, and a pong with that nonce from the friend is on the bus within Within. The unit tier runs it over a fake Exec and bus's Fake; nova-friend check and install run it against the live session.

func (*Conformance) Run

func (c *Conformance) Run(ctx context.Context) CheckResult

Run delivers the check and reads for its pong until Within has passed. A Stub's refusal, a Deferred and a nonzero exit are stage deliver, each with the adapter's own reason.

type CopyFile

type CopyFile func(src, dst string) error

CopyFile copies src onto dst. CopyExecutable is the one install uses; a test passes its own, and nil refuses a removable-volume binary.

type Courier

type Courier struct {
	// Bus is the bus every send goes on; Open, when set, dials one for each
	// send instead (the server's way: one connection per note) and its
	// failure is watched like a send's.
	Bus   *bus.Bus
	Open  func(ctx context.Context) (*bus.Bus, func(), error)
	Watch *bus.Watch
	Now   func() time.Time
}

Courier is how the sprint server sends its notes to friends: each send on the bus, its result watched, so a store that refuses the login or cannot be reached is one alarm to the coordinator, raised at the first failure and cleared at the next success, and never only a log line (the finding of 2026-10-04: 54 failures in 30 minutes, WRONGPASS for the server's bus user, and nothing said so; docs/SPEC-FRIEND.md, fr-delivery-receipts.w1). Every note is owed its friend's receipt like any message to her (docs/SPEC-BUS.md, fr-delivery-receipts.w1).

func (*Courier) Send

func (c *Courier) Send(ctx context.Context, m bus.Message) (bus.Message, error)

Send sends m and hands its result to the Watch.

type DSH

type DSH struct {
	Dir, Session string
	Run          Exec
	Program      string    // DSHProgram when empty
	Sessions     string    // the sessions root; DSH_HOME/sessions, else ~/.dsh/sessions, when empty
	Out          io.Writer // where the turn's output goes, when set: the daemon's record
	// contains filtered or unexported fields
}

DSH delivers through `dsh headless --session-id <id> -` run in Dir, the text on stdin: the headless profile adopts the persisted session (the same session directory under ~/.dsh/sessions/<key>/<id>, its record grows by one turn; measured 2026-10-04, v0.2.0-rc.2) and exits when the turn ends. An unknown id, a session recorded in another directory, and a missing provider key each exit 1. Without a session named, the newest session of Dir from the store (DSH_HOME, else ~/.dsh). The desktop app the friend sits in shares the store. Measured 2026-10-04 and 2026-10-05 (docs/SPEC-FRIEND.md, the dsh row): a session under an agent preset is refused by the one-shot runner whatever the text, exit 1 before any write, its transcript hash unchanged (the runner adopts only a session with no preset, and a session never returns to none). Measured 2026-10-06: the same refusal also comes with exit 0. The refusal, and MISSING_CREDENTIAL, are read from the output whatever the exit (DSHRefusal): a SessionRefused, so the message stays pending instead of being given up after three refusals, and the daemon marks the session broken at once until a turn succeeds. On the survey machine, deliver.log records 1339+ deferred deliveries against Zhi's real open session, and the desktop app exposes no local listener or IPC socket. No route into the open desktop session exists, so Route answers defer; the session reads the bus itself with nova-bus wait or nova-bus recv.

func (*DSH) Deliver

func (d *DSH) Deliver(ctx context.Context, text string) (int, error)

func (*DSH) Route

func (d *DSH) Route(ctx context.Context) (route, line string, err error)

Route is what status and check say: push. Every delivery goes into the friend's session as a headless turn (dsh headless --session-id), the session check included; none waits on the open desktop app, which no route reaches (docs/SPEC-FRIEND.md, the dsh row). line is that turn's command.

func (*DSH) SpentUSD added in v1.2.14

func (d *DSH) SpentUSD() float64

SpentUSD is the cost of every run so far, in US dollars, as the harness's own usage report says it (docs/SPEC-FRIEND.md, spend caps). Each turn's output is read for the cost it names (dshCost): the stream-json result line's total_cost_usd, else a `cost_usd=<n>` token. A turn that names no cost is not counted, and the provider's funds refusal stops the daemon instead of the cap.

type Daemon

type Daemon struct {
	Friend, Harness, Dir string
	// Pro holds the friend's pronouns (the row's pronouns setting): every text the daemon
	// writes about the friend says them through d.say. Nil is they/them.
	Pro              *PronounsRef
	Notices          *Notices
	Store            bus.Store
	Deliver          Deliverer
	Beat             func(ctx context.Context, active time.Time) error // one beat to the sprint server, carrying the session's last activity (zero: none known)
	StepBeatForTests bool                                              // deterministic fake-clock seam; production has one independent beat caller
	// Activity is the newest write of the session's files, read by the
	// independent beat cadence at most once an ActivityEvery and carried on
	// each beat; nil knows no write.
	Activity func() time.Time
	Now      func() time.Time
	// Pause waits between lane steps while the independent beat continues.
	Pause  func(ctx context.Context, d time.Duration)
	Record func(line string) // one line per delivery, to the daemon's log
	// MachineStopped says the machine's word, as the owner last read it off the beat's
	// answer (ParseMachine), is STOPPED: the lanes cancel what runs, owe and send
	// stop-returns, and start nothing (stop.go). nil: never stopped.
	MachineStopped func() bool
	// StopReturn sends one stop-return (StopReturnArgv) to the sprint server; nil sends it
	// through Sprint.
	StopReturn func(ctx context.Context, argv []string) error

	// Limited is the harness's limit now: its kind and reset, and whether there is one
	// (Limits.Limited, Limits.Kind); nil: none. While there is one the status says
	// session=limited with them, and the turns the Gate defers stay pending.
	Limited func() (kind string, until time.Time, limited bool)
	// Status receives the daemon's state whenever it changes, and every
	// StatusEvery (WriteStatus over the state files).
	Status func(Status) error
	// SilentStop is how long a running turn may print nothing before it is
	// stopped (DefaultSilentStop when zero); BrokenAfter how many turns in a
	// row the provider refuses the same way before the session is broken
	// (DefaultBrokenAfter when zero); Coordinator who is told of a broken
	// session when the sprint server names no seat.
	SilentStop  time.Duration
	BrokenAfter int
	Coordinator string
	// Row is the friend's nova-config row as the daemon last read it (from
	// its beat): her delivery mode (ModeBatch or ModeOneShot) and width,
	// read every step so a change takes effect without a restart; nil, or
	// empty answers, deliver in batch, and a width of 0 runs no lane: the width
	// is nova-config's alone, answered by the server (row).
	Row func() (mode string, width int)
	// Pacing is the row's pacing as the daemon last read it: the fraction of each
	// subscription window the lanes may spend, read every step; nil, or out of
	// (0, 1], is DefaultPacing (pacing.go). No beat carries the row's pacing yet,
	// so nova-friend leaves it nil.
	Pacing func() float64
	// LaneCaps is the row's wall cap of a lane's card by its tier as the daemon last read
	// it (ParseLaneCaps off its beat), read every step; nil, or a tier it names none for,
	// is DefaultLaneCaps (lane_cap.go).
	LaneCaps func() map[string]time.Duration
	// LoadLanes and SaveLanes let tests carry lane state between loop instances.
	// Production keeps that state in memory; the sprint server owns lane records.
	LoadLanes func() (LaneState, error)
	SaveLanes func(LaneState) error
	// CardDone is the one bus line a lane's session sends when its card is
	// done (nova-bus send by path, as this friend, to the coordinator).
	CardDone func(card, to string) string
	// Progress stamps progress on the cards whose lane turn printed (ProgressArgv to the
	// sprint server); nil stamps none.
	Progress func(ctx context.Context, cards []Card) error
	// Finish sends one finish verb to the sprint server: a lane's card whose run ended with
	// no REPORT.md (FinishArgv, lane_end.go), and every working card on her row whose job's
	// REPORT.md says a verdict, whoever wrote its brief (OutboxFinishArgv, outbox.go). Nil,
	// or a finish not answered, leaves it to friend sync, which reads the same REPORT.md.
	Finish func(ctx context.Context, argv []string) error
	// Take sends one take to the sprint server (LaneTakeArgv), its refusal an
	// error: a lane starts a card only once the server has it working on her row (lane_take.go).
	// Nil (a test's world) takes nothing, and a lane runs any card on her row as before.
	Take func(ctx context.Context, argv []string) (string, error)
	// The lanes' parity with the runner scripts they replace (lane_parity.go); Rules nil
	// turns every one off. Rules is her row's lane rules as her beat last answered them,
	// Load the machine's one-minute load (nil: no load rule), Tokens a session's tokens
	// from opencode's database (nil: no cost and no token cap), Route the store's route row
	// for her model and Model its name, LaneHold the pause marker's line (the lanes held
	// down by a provider failure until a person clears it) and LaneHoldDown writes it: her
	// beat then says her down with its message (PauseBeat).
	Rules        func() LaneRules
	Load         func() float64
	Tokens       func(ctx context.Context, session string) (LaneTokens, error)
	Route        func() RoutePrice
	Model        string
	LaneHold     func() string
	LaneHoldDown func(ctx context.Context, message string) error
	// Fault is the lane's provider-refusal fault report in the state directory
	// (ReadLaneFault), and FaultWrite records or clears it (WriteLaneFault): the
	// loop's poll sees a fault it holds whose file a person cleared and releases
	// the lanes, and a stale file a restart cleared. The friend's main wires it
	// to the state directory; nil (a test's world) leaves the fault in memory
	// only, cleared by a restart (docs/SPEC-FRIEND.md, the lanes).
	Fault      func() string
	FaultWrite func(report string) error
	// FaultExternal is a lane fault the limit detector counted itself (lanes
	// failing alike): the loop adopts it as the same record a provider's refusal
	// writes, then clears it. The friend's main wires it to the detector; nil:
	// no such fault.
	FaultExternal func() string
	// FaultDown marks her row down until until with reason: the same harness fault
	// FaultRepeats times within FaultWithin on her lanes (lane_parity.go); her beat says
	// her down with them until then (friend beat --until --reason). Nil: her lanes are
	// held here alone.
	FaultDown func(until time.Time, reason string)
	// RefusalDown marks her row down until until with reason: a server that refused one
	// of her verbs RefusalRepeats times in a row (inbox.go, "A refused verb"). RefusalUp
	// clears it when the server answers again. Nil: the refusal is said, never beaten down.
	RefusalDown func(until time.Time, reason string)
	RefusalUp   func()
	// Prove runs the harness's cheapest no-tool turn and answers its terminal reason
	// (harness_proof.go): "" when the harness can answer, the reason when it cannot. Nil
	// proves nothing (every harness can answer; a test's world). Until it answers, the
	// daemon takes no card, beats down with the reason, tells the coordinator once, and
	// re-proves every ServedEvery (docs/SPEC-FRIEND.md, "A lane proves its harness").
	Prove func(ctx context.Context) string
	// Held is every card on her row as the sprint server says it (HeldVia: friend cards
	// <friend>, else the worker view), asked once an InboxEvery; her inbox is reconciled with
	// the answer (SyncInbox, inbox.go). Nil leaves her inbox to friend sync alone.
	Held func(ctx context.Context) (Row, error)
	// Sprint sends one verb to the sprint server and answers what it printed: the reader row's
	// queue, begin, verdict and return (read_lanes.go). Nil runs no reads. ReadSlots is the
	// row's read slots, read each step (nil: DefaultReadSlots), and ReadModel the model of a
	// read's tier ("": the harness's own).
	Sprint    func(ctx context.Context, argv []string) (string, error)
	ReadSlots func() int
	ReadModel func(tier string) string
	// Stage stages a held work card's job (Stager.Stage: jobs/<job>/repo and its JOB.md) and
	// answers the commit staged (stage.go); nil stages none, and a lane is handed a card with
	// its brief alone.
	Stage func(ctx context.Context, p Packet) (string, error)
	// Prune removes finished jobs' worktrees past FinishedJobsKept (Stager.Prune), given the
	// jobs that are live (held on her row, run by a lane, being staged), after each inbox
	// cleanup, and answers the jobs it removed; nil prunes none.
	Prune func(ctx context.Context, live map[string]bool) ([]string, error)
	// Tip is origin's tip of a branch of a repository (owner/name), "" when origin has no
	// such branch (Stager.Tip: one git ls-remote): a report's LAND finishes only at that tip,
	// as nova-sprint collect's does (outbox.go). Nil reads none, and a LAND finishes at its
	// Head.
	Tip func(ctx context.Context, repo, branch string) (string, error)
	// Running is the beat's running list as the sprint server last said it, every friend's:
	// a card id or job to the friend whose lane runs it. A lane is never started for a card
	// it names another friend running, and a lane whose card left her row names its friend
	// on the job's lane mark (one_lane.go). Nil says none; the lane marks still hold.
	Running func() map[string]string
	// Holders reads the server's current card-to-holder map (view cards), once
	// per outbox pass that finds a report outside her row. Nil uses Running's
	// known holders; an error refuses with the explicit unknown-holder remedy.
	Holders func(ctx context.Context) (map[string]string, error)
	// Seat is the coordinator seat holder as the sprint server says it: who the
	// daemon tells (coordinator), else Coordinator. An error, an empty name or a
	// nil Seat is the seat unknown.
	Seat func(ctx context.Context) (string, error)
	// contains filtered or unexported fields
}

Daemon runs one friend's lanes, reader slots, and sprint metadata beat. Session messages belong to nova-hook, which owns the bus delivery lease. Everything it reaches outside itself is a field, so a test runs it over bus's Fake, a fake harness and its own clock.

func (*Daemon) Idle added in v1.2.14

func (d *Daemon) Idle() bool

Idle reports whether the last step has no running lane or read. The spend cap uses it to avoid launching work while there is nothing to run.

func (*Daemon) OwedStopReturns

func (d *Daemon) OwedStopReturns() int

OwedStopReturns is how many stop-returns the lanes owe now: the beat carries it.

func (*Daemon) Run

func (d *Daemon) Run(ctx context.Context) error

Run is the loop until ctx ends. Each step: the clock; the friend's row (Row: her mode and width); each running lane turn's output watched, and a turn silent past SilentStop stopped; the ended lane turns, reads and finishes applied; her inbox reconciled with her row; then, in one-shot mode, each free lane handed its next card (lanes.go) and the reader row served (read_lanes.go); an independent beat; the status. The daemon reads nothing off her bus stream and delivers nothing into her session: a message to her reaches her through nova-hook (pkg/harness/notice), and whether she is there is the nova-hook record's alone.

type DaemonFacts

type DaemonFacts struct {
	Friend   string `json:"friend"`
	Agent    string `json:"agent"`    // loaded, not-loaded, none
	PID      string `json:"pid"`      // numeric string or "-"
	Status   string `json:"status"`   // the daemon's status file: ok, stale, none
	Hook     string `json:"hook"`     // the hook record's state (none, armed, proven, unhooked, ...), or "-" when not read
	Since    string `json:"since"`    // how long it has been in it, e.g. "4m", or "-"
	Answer   string `json:"answer"`   // the age of her model's last answer to a challenge, or "-"
	Misses   int    `json:"misses"`   // challenges in a row left unanswered
	Presence string `json:"presence"` // up while the record proves her there, else down
	Reason   string `json:"reason"`   // why the record reads down, or "-"
}

DaemonFacts carries facts about the launchd agent, the daemon, and her nova-hook record: the one truth of whether she is there.

func (DaemonFacts) Line

func (df DaemonFacts) Line() string

Line renders the CHECK DAEMON line.

type Deferred

type Deferred struct{ Reason, Remedy string }

Deferred means the harness cannot take this turn now. Remedy names what must change when retry alone cannot succeed (PushProof).

func (Deferred) Error

func (d Deferred) Error() string

type Deliverer

type Deliverer interface {
	Deliver(ctx context.Context, text string) (exit int, err error)
}

Deliverer pushes one text through a harness for delivery checks and reach. LaneHarness and CardRunner extend it for the daemon's card turns.

func NewDeliverer

func NewDeliverer(harness, dir, session string, run Exec, out io.Writer) (Deliverer, error)

NewDeliverer is the adapter for harness, in the friend's directory, into session (empty: the newest session of that directory where the harness can name one). An unknown harness is refused with the names there are.

func SelectDeliverer

func SelectDeliverer(friendName, harness, dir, session, adapter, deliveryDir string, run Exec, out io.Writer) (Deliverer, error)

SelectDeliverer chooses the explicit folder route, or the harness's supported adapter. Codex has no implicit delivery route (SPEC-FRIEND.md, Codex).

type Entry

type Entry struct {
	Kind   string // KindDir, KindFile, KindSymlink or KindMissing
	Target string // a symlink's target
}

Entry is what a path is, the path itself and never what a symlink names.

type Evidence

type Evidence struct {
	DaemonUp    bool       // the daemon's status file is fresh; shown, never deciding up
	Hook        HookRecord // her nova-hook record: the one truth of whether she is there
	Limit       string     // the limit's name, when the provider said one
	LimitUntil  time.Time  // the limit's reset; zero, or past, is no limit
	Undelivered int        // messages waiting on the friend's stream; negative is not counted
	BusBlocked  string     // why the bus cannot deliver to the friend; empty when it can
	LastResult  time.Time  // the end of the last turn; zero is none yet
	LastExit    int        // that turn's exit
}

Evidence is what a friend's status is decided from.

type Exec

type Exec func(ctx context.Context, dir, name string, args []string, stdin string) (stdout string, exit int, err error)

Exec runs one command for an adapter: the program, its arguments and its working directory, with the text on stdin, answering what it printed and its exit code. The daemon passes the real one (RealExec); a test its own.

func ShimExec

func ShimExec(run Exec, shimDir, pathEnv string) Exec

ShimExec is run with the shim directory first on the PATH of every lane child and GOROOT pointing nowhere: a command whose context is a lane's runs as `env PATH=<shims>:<path> GOROOT=<nowhere> <name> <args>`; any other runs as it was. Put it outside Wall.Exec so the env runs inside the wall.

type FaultWatch

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

FaultWatch counts one row's harness faults: the FaultRepeats-th of the same fault within FaultWithin is one down until FaultDownFor later; a fault while that down stands adds nothing, and the count starts again once it has passed. The zero value is ready; it reads no clock of its own.

func (*FaultWatch) Observe

func (w *FaultWatch) Observe(fault string, now time.Time) (until time.Time, down bool)

Observe adds one fault at now and answers the down the row is owed, once: until and true on the FaultRepeats-th of that fault within FaultWithin.

type Folder

type Folder struct {
	Dir, Session, Friend, WorkDir string
}

Folder is an explicit delivery route for a real Codex session whose watcher reads Dir. Writing a file is acceptance by the folder, never a session pong. The native proof still requires the session's own nonce-bearing bus reply.

func (*Folder) Deliver

func (f *Folder) Deliver(ctx context.Context, text string) (exit int, retErr error)

type FriendCheck

type FriendCheck struct {
	Friend  string       `json:"friend"`
	Daemon  DaemonFacts  `json:"daemon"`
	Harness HarnessFacts `json:"harness"`
	Bus     BusFacts     `json:"bus"`
	Work    WorkFacts    `json:"work"`
	Verdict VerdictFacts `json:"verdict"`
}

FriendCheck is the full fact sheet for one friend.

func CheckFriend

func CheckFriend(ctx context.Context, friendName string, seams CheckSeams, since time.Duration, shown *ShownEntry) FriendCheck

CheckFriend gathers the facts through the seams over the window and decides the verdict for one friend (docs/SPEC-FRIEND.md "Check").

func (FriendCheck) Lines

func (fc FriendCheck) Lines() []string

Lines renders the five CHECK lines for one friend.

type GUIWindow

type GUIWindow struct {
	Bundle    string
	Run       Exec
	Permitted func(ctx context.Context) (bool, error)
}

GUIWindow types one message into a GUI harness's composer and submits it. Permitted, when set, answers whether the accessibility permission is held. Nil asks the platform (PlatformPermitted), which never prompts.

func (GUIWindow) Deliver

func (g GUIWindow) Deliver(ctx context.Context, text string) error

Deliver types text once the permission is held (docs/SPEC-FRIEND.md, Reach; tla/Reach.tla, the window step). Absent permission is WindowRefused. The tool does not ask.

type Gemini

type Gemini struct {
	Dir, Session string
	Run          Exec
	Program      string    // "gemini" when empty
	Out          io.Writer // where the turn's output goes, when set: the daemon's record

}

Gemini delivers through `gemini --skip-trust --resume <id> --prompt=<text>` in Dir, which resumes the persisted session (the same session id and the same chat file under ~/.gemini/tmp/<project>/chats/, read from the CLI's ChatRecordingService.initialize on 2026-10-04, v0.46.0) and blocks for the whole turn. Without a session named, "latest": the CLI's own newest session of the project that Dir is, so a friend who starts a fresh session is still reached. --skip-trust trusts Dir for this process only; the friend's own session already trusts it. The text goes as --prompt=<text>, one argument, so a message beginning with a dash is never read as a flag. Exit codes are the CLI's: 42 when the session is not found, 55 untrusted, 1 an error.

func (*Gemini) Deliver

func (g *Gemini) Deliver(ctx context.Context, text string) (int, error)

type Grok

type Grok struct {
	Dir  string    // the friend's directory: the session's cwd
	Wake string    // the wake file, when named; else the one the session's monitor tails
	Run  Exec      // runs ps
	Out  io.Writer // the daemon's record, when set
	Home string    // the grok home, ~/.grok when empty
	// contains filtered or unexported fields
}

Grok delivers into the Grok Build TUI (xAI's `grok`), which has no deliver verb, no leader socket unless leader mode is on, and no way to send into an open window: `grok -p <text> --resume <id>` runs the turn in a second process over the same transcript, not in the window the friend is in. What the window has is the monitor tool: a background task whose every new output line "becomes a notification delivered to the conversation" and wakes the agent for a turn (the harness's own guide, ~/.grok/docs/user-guide/20-background-tasks.md). The friend's session runs one over a wake file, `tail -n 0 -F <file>.wake`, and a line appended to that file arrives in the session as a <monitor-event> user turn (measured 2026-10-04 in a friend's session: a user_message_chunk at a new promptIndex in its updates.jsonl). So Deliver appends the text, as one line, to the wake file the open session's monitor is tailing. A backlog is many Deliver calls. Lines written with no gap are the flood that stops the monitor, so each line after the first waits out what remains of wakePace. It is accepted (exit 0) once the line is in the file and the tail was running under that session; the turn runs after Deliver returns, since nothing hands its end back. While no monitor runs (no window, or a window with no tail) Deliver defers: nothing is written, the message stays pending, and the reason carries the one line the session runs. A wake path that is not absolute is a refusal and nothing is written.

func (*Grok) Deliver

func (g *Grok) Deliver(ctx context.Context, text string) (int, error)

func (*Grok) Route

func (g *Grok) Route(ctx context.Context) (route, line string, err error)

Route is what status says. push: a tail of an absolute .wake file runs under the open window's pid, so a delivery now is a turn in that window. defer: no such tail; line is the monitor line the session runs. A listing or session file that cannot be read is defer, never a silent push, and err says why.

type HarnessFacts

type HarnessFacts struct {
	Friend         string `json:"friend"`
	Harness        string `json:"harness"`
	Route          string `json:"route"` // push, mailbox, queue or passive
	Last           string `json:"last"`  // RFC3339 or "-"
	LastExit       string `json:"last_exit"`
	FailedOfLast20 int    `json:"failed_of_last20"`
	Deferred       int    `json:"deferred"`
	Delivered      int    `json:"delivered"` // deliveries in the window (JSON only)
	Failed         int    `json:"failed"`    // of those, the ones that failed (JSON only)
	Broken         string `json:"broken"`    // RFC3339 or "-"
	Reason         string `json:"reason"`    // one line or "-"
}

HarnessFacts carries facts about the harness, deliveries and breaks: every delivery count is of the --since window (docs/SPEC-FRIEND.md "Check").

func (HarnessFacts) Line

func (hf HarnessFacts) Line() string

Line renders the CHECK HARNESS line.

type HarnessSettings

type HarnessSettings struct {
	Harness, Friend, Dir string
	Home                 string // the friend's home: ~ of every default below
	StateDir             string // the daemon's state directory; DefaultStateDir when empty (grok's default wake file)
	Wake                 string // grok: the wake file the session's monitor tails; <state>/<friend>.wake when empty
	ConfigDir            string // claude: CLAUDE_CONFIG_DIR, the friend's own config directory
	Model                string // opencode: the model, provider/model; empty leaves it alone
	CodexHome            string // codex: CODEX_HOME; Home/.codex when empty
	DSHHome              string // dsh: DSH_HOME; Home/.dsh when empty
	FS                   SettingsFS
}

HarnessSettings is what install writes for one friend's harness and what check compares: the friend's directory and, per harness, the flags that name a setting. The writer of each harness is one small function (settings_<harness>.go).

func (HarnessSettings) Check

func (h HarnessSettings) Check() ([]Setting, error)

Check is every setting with what is there now; nothing is written.

func (HarnessSettings) Plan

func (h HarnessSettings) Plan() ([]Setting, error)

Plan is what Write would write, with Write's refusals; nothing is written (install --dry-run).

func (HarnessSettings) WakePath

func (h HarnessSettings) WakePath() string

WakePath is the wake file these settings name for grok.

func (HarnessSettings) Write

func (h HarnessSettings) Write() ([]Setting, error)

Write makes every setting what install wants and answers the ones it wrote. Every path is checked first: a symlink, the wrong kind, or a missing path install does not make is refused (ErrNotRealDir) and nothing is written. Each write is read back; a setting that still differs is an error naming it.

type HeldAnswer

type HeldAnswer struct {
	Friend string     `json:"friend"`
	Cards  []HeldCard `json:"cards"`
}

HeldAnswer is the server's answer to friend cards <friend>: every card on her row, the ready ones dealt behind her working ones and her reads among them.

type HeldCard

type HeldCard struct {
	Card    string `json:"card"`
	Job     string `json:"job"`
	Col     string `json:"col"`
	Kind    string `json:"kind,omitempty"`
	Branch  string `json:"branch,omitempty"`
	Tier    string `json:"tier,omitempty"`
	Stream  string `json:"stream,omitempty"`
	Attempt int    `json:"attempt,omitempty"`
	Gen     int    `json:"gen,omitempty"`
	Epoch   uint64 `json:"epoch"`
	Repo    string `json:"repo,omitempty"`
	Base    string `json:"base,omitempty"`
	Brief   string `json:"brief"`
	// Lane is the lane of her row the server says holds the card (take --lane), 0 for none.
	Lane int    `json:"lane,omitempty"`
	Why  string `json:"-"`
}

HeldCard is one card on the friend's row as the server answers it: the card, the job it is delivered as (inbox/<job>), its column (ready or working) and its BRIEF.md whole, with its packet (its kind, work or read, the branch it is pushed to, its tier, attempt, generation and epoch, and the repository and base its checkout is staged from: PacketOf reads the brief's REPO: and BASE: lines when the server sends neither); Why is, for a card the answer sends no brief for, why not (the worker view's, ParseView).

func ParseHeld

func ParseHeld(friend, out string) ([]HeldCard, error)

ParseHeld reads the server's answer to FriendCardsArgv: its one JSON line, for this friend.

func ParseView

func ParseView(friend, out string) ([]HeldCard, error)

ParseView reads the worker view's answer for this friend: each work card on her row, its job the inbox directory its brief path names, and no brief (Why says so).

type HookRecord added in v1.3.16

type HookRecord struct {
	State      string    // none, armed, proven, unhooked, ...; "" when not read
	Since      time.Time // when it was hooked, or unhooked; zero when never
	LastAnswer time.Time // when her model last answered a challenge; zero is never
	Misses     int       // challenges in a row left unanswered
	Down       string    // why it reads down whatever its state (unhooked, no server, no answer), "" when it stands
	ReadError  string    // why the record could not be read; "" when it was
}

HookRecord is a friend's nova-hook record as status and check read it (nova-tools hook.Status): its state, since when, the last answer to a challenge, the challenges missed in a row, and why it reads down.

func (HookRecord) Up added in v1.3.16

func (h HookRecord) Up() bool

Up says the record proves her there: proven, and nothing reads it down.

func (HookRecord) Words added in v1.3.16

func (h HookRecord) Words(now time.Time) string

Words is the record as a person reads it on one line.

type HostRefused

type HostRefused struct{ Session string }

HostRefused is host's refusal: the session runs already.

func (HostRefused) Error

func (h HostRefused) Error() string

type HostResult

type HostResult struct {
	Session, Attach, Line string
}

HostResult is what host started (or, on a dry run, would start).

func Host

func Host(ctx context.Context, run Exec, s HostSpec) (HostResult, error)

Host starts the command in a new detached tmux session friend-<name> in the directory, refusing when that session exists. A dry run runs no tmux.

type HostSpec

type HostSpec struct {
	Name, Dir string
	Command   []string
	DryRun    bool
}

HostSpec is one host: the friend, its directory, the launch command and whether to only print.

type Hosted

type Hosted struct {
	Session string `json:"session"`
	Harness string `json:"harness"`
	Prompt  string `json:"prompt"`
}

Hosted is what host saves so run and install need no flag: the tmux session, the harness it hosts and the idle prompt pattern (source text).

func ReadHost

func ReadHost(stateDir string) (h Hosted, found bool, err error)

ReadHost is the saved host state; found is false when host never ran.

type InboxCounts

type InboxCounts struct {
	Held, Inbox, Missing int
}

InboxCounts is what one reconcile found: the cards held on her row, the sprint jobs in her inbox, and the held cards whose BRIEF.md is still not there after it.

func SyncInbox

func SyncInbox(dir string, row Row, keep map[string]bool, asked, now time.Time, record func(string)) (InboxCounts, error)

SyncInbox reconciles her inbox under dir with row, the cards on her row as the server answered an ask begun at asked: each held card's inbox/<job>/BRIEF.md is written when it is not there (written whole, never over a file there; a rework's with its fix first, ReworkedBrief), and each sprint job in the inbox that no held card names, that no lane runs (keep), whose kind the answer covers (row.Reads), and whose BRIEF.md was written before asked, is moved to inbox/retired/. A brief written since the ask began is friend sync's for a card dealt after the server answered, and the next pass decides it (without this, the TLA+ model InboxReconcile retires a held card's brief: docs/SPEC-FRIEND.md). asked is the wall clock, as the file times are; zero retires none. record gets one line per write and per retirement, and one per held card it could not write. The counts are what the inbox holds after the pass.

type LaneEnd

type LaneEnd struct {
	Exit     int
	Err      string        // the harness's error, when the run did not end with an exit
	Wall     time.Duration // the run's wall, its start to its end
	Cap      string        // the cap that stopped it, when one did
	Rejected string        // a permission the harness refused
	Turns    int           // the turns the card had in the lane
	Restart  time.Time     // not zero: the run was found gone by a daemon starting up at this time
	Started  time.Time     // when the lane began the card
	NoReport bool          // the run wrote RESULT.md and no REPORT.md
	// Capped is the card's wall cap by its tier when the lane ended the card at it
	// (lane_cap.go), Tier that tier, Overrun the card's wall past the cap at its end, and
	// Tail the last CapTailLines lines of the lane's output.
	Capped  time.Duration
	Tier    string
	Overrun time.Duration
	Tail    string
}

LaneEnd is how a card's last run in a lane ended, as its finish says it.

func (LaneEnd) How

func (e LaneEnd) How() string

How is the run's end on one line: the restart that found it gone, else the cap, the refused permission, the error or the exit, and the wall.

type LaneFaultWatch added in v1.2.14

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

LaneFaultWatch counts one lane's provider refusals: the LaneFaultAlike-th of the same refusal is the fault, with the first refusal line; a different line starts the count again. The zero value is ready.

func (*LaneFaultWatch) Clear added in v1.2.14

func (f *LaneFaultWatch) Clear()

Clear clears the lane fault, the release: the lane may take cards again and a fresh run of alike refusals is counted anew. The loop calls it on a restart and when the fault file a person removed is gone (docs/SPEC-FRIEND.md, the lanes).

func (*LaneFaultWatch) Faulted added in v1.2.14

func (f *LaneFaultWatch) Faulted() bool

Faulted answers whether the lane is faulted: after the LaneFaultAlike-th identical provider refusal the lane holds its card; it is released only by a restart or a clear (docs/SPEC-FRIEND.md, the lanes).

func (*LaneFaultWatch) Observe added in v1.2.14

func (f *LaneFaultWatch) Observe(line string) (string, bool)

Observe adds one provider-refusal line and answers the lane fault on the LaneFaultAlike-th identical one, once per run of alike refusals.

func (*LaneFaultWatch) Report added in v1.2.14

func (f *LaneFaultWatch) Report() string

Report is the lane-fault report (lane-fault=<n> refusal="<first line>") the lane last faulted on, for the daemon's beat; "" when the lane is not faulted (docs/SPEC-FRIEND.md, the lanes).

func (*LaneFaultWatch) Set added in v1.2.14

func (f *LaneFaultWatch) Set(report string)

Set marks the lane faulted with report as given, for a detector that counted its own bound (the limit detector's lanes failing alike): the same record a provider refusal's Observe writes, never counted again.

type LaneGovernor

type LaneGovernor struct {
	Rand func() float64   // pseudo-random in [0, 1); nil is math/rand/v2.Float64 (docs/SPEC-FRIEND.md #rate-limit-backs-off-not-down.w1)
	Now  func() time.Time // clock; nil is time.Now
	// contains filtered or unexported fields
}

LaneGovernor is the lanes' live cap under rate limits, and their hold when out of funds. Its zero value is the row's width, never paused. RateLimit lowers the cap by a quarter (at least one lane, never below one) and pauses new turns for the backoff, which doubles from RateBackoffFirst to RateBackoffMax; turns that started before the last lowering are the same episode and change nothing. Step resumes after the pause and raises the cap one lane per clean RateRaiseEvery, measured: a turn ended clean in it (Clean). Back at the row's width the backoff starts over. Each change is one line.

func (*LaneGovernor) Cap

func (g *LaneGovernor) Cap(width int) int

Cap is the live cap at width: the row's width unless a rate limit has lowered it, never above the row.

func (*LaneGovernor) Clean

func (g *LaneGovernor) Clean(now time.Time)

Clean is a lane turn that ended at now without a rate limit: the measurement a raise waits for.

func (*LaneGovernor) Held

func (g *LaneGovernor) Held() string

Held is why the lanes are held, out of funds; "" when they are not.

func (*LaneGovernor) Hold

func (g *LaneGovernor) Hold(reason string) bool

Hold is out of funds: the lanes start nothing more until the daemon restarts. It answers whether this is the first hold, the one to say.

func (*LaneGovernor) PauseUntil

func (g *LaneGovernor) PauseUntil(until time.Time, reason string, now ...time.Time) string

PauseUntil is a usage limit with its reset: no new turn or open before until, jittered by adding up to 20% of the remaining wait, never earlier than the reported reset itself (docs/SPEC-FRIEND.md #rate-limit-backs-off-not-down.w1). The cap is left alone. It answers the line that says it, empty when the pause already reaches until.

func (*LaneGovernor) Paused

func (g *LaneGovernor) Paused(now time.Time) bool

Paused says whether no new turn or open may start now: a backoff under way, or out of funds.

func (*LaneGovernor) RateLimit

func (g *LaneGovernor) RateLimit(now, started time.Time, width int, reason string) (line string, judge bool)

RateLimit is a rate limit met at now by a turn (or open) started at started, at the row's width: the line that says the change, and whether it is a judgment (the RateJudgeAfter-th lowering within RateJudgeWithin). A turn started before the last lowering is the same episode: no line. Every lane resume after a rate limit is spread by a jitter of +/-20% of the pause (docs/SPEC-FRIEND.md #rate-limit-backs-off-not-down.w1).

func (*LaneGovernor) Release

func (g *LaneGovernor) Release()

Release lifts a hold: a person brought the lanes up (the pause marker is gone).

func (*LaneGovernor) Step

func (g *LaneGovernor) Step(now time.Time, width int) []string

Step is the governor at now and the row's width: the pause's end said once, and a clean RateRaiseEvery with a clean turn in it raising the cap one lane; back at the width, the cap is the row's again and the backoff starts over. It answers the lines of what changed.

type LaneHarness

type LaneHarness interface {
	Deliverer
	// OpenSession starts a new session of the friend with seed as its first
	// turn, and answers the new session's id.
	OpenSession(ctx context.Context, seed string) (session string, err error)
	// DeliverTo pushes text into session as one turn and blocks until it
	// ends: its exit, and a permission the harness refused without asking,
	// read from the turn's output (empty when none). A rate limit is
	// RateLimited and out of funds OutOfFunds (ProviderLimit); a provider's
	// refusal is ProviderRefused, as Deliver answers it.
	DeliverTo(ctx context.Context, session, text string) (LaneTurn, error)
}

LaneHarness is a Deliverer that can open a session of the friend and deliver into a session it names: what a one-shot lane needs, each lane its own session in the same harness and directory (docs/SPEC-FRIEND.md, one-shot lanes; the owner, 2026-10-04: "so [she] can still be wide, it's just 8 [of her]"). OpenCode is one.

type LaneHold added in v1.2.7

type LaneHold struct {
	Lane  int
	Epoch uint64
	Card  string
	Gen   int
	Kind  string
	Job   string
}

LaneHold is the server's answer to a lane's ask: the lane, the epoch the server holds, and the card the lane holds there (Card "" for none) at its generation, its kind (work or read) and its job (<id>~<epoch>, .g<gen> from the second generation).

func ParseLaneHold added in v1.2.7

func ParseLaneHold(member string, n int, out string) (LaneHold, error)

ParseLaneHold reads the server's LANE line for member's lane n out of an ask's answer. An answer with no such line, or one naming another member or lane, is a refusal: the lane runs nothing (the fault of 2026-10-10 21:42Z: a take answered OK and the lane said "took" while the server still showed the card ready). A line for this lane that holds a card but lacks its generation, kind or job is Unparseable, naming the field (the fault of 2026-10-14 03:51Z: rank= between holds= and kind= left a reader-only friend's lanes refusing every read card's line, once every TakeRetry, all night).

type LaneJob

type LaneJob struct {
	Dir   string // <friend dir>/jobs/<job>: the harness's working directory
	Card  Card   // Brief and Outbox absolute
	Brief string // the brief's text, inline in the prompt; "" when it is not read yet
}

LaneJob is a card as its lane hands it to the harness: the job directory the harness runs in, the card with its brief and outbox absolute, and the brief's text, which the prompt carries.

func LaneJobOf

func LaneJobOf(dir string, c Card, abs func(string) (string, error)) (LaneJob, error)

LaneJobOf is card c of the friend's working directory dir as its lane hands it: every path made absolute by abs (filepath.Abs in the daemon), the job directory dir/jobs/<job>. A path abs cannot make absolute is refused: the error is one REFUSED line naming the path, and the lane does not start.

type LaneMark

type LaneMark struct {
	Who   string
	At    time.Time
	Ended bool
}

LaneMark is a job's lane mark as read: Who runs it (Ended false) or ended it (Ended true), and At, when a running mark was last refreshed.

func ReadLaneMark

func ReadLaneMark(dir, job string) (m LaneMark, found bool)

ReadLaneMark is the job's lane mark; found is false when there is none or it says neither word.

type LaneRules

type LaneRules struct {
	Tiers     []string // row_tiers=flash,pro: the tiers she works; a dealt card of another tier is taken back; none: every tier
	Streams   []string // row_streams=security*,fp-sec*: patterns a card's stream or id must match; none: every card
	TokenCap  int64    // row_token_cap=<n>: tokens one card may spend, all kinds; 0 none
	LoadMax   float64  // row_load_max=<load>: above this one-minute load the lanes are held to LoadWidth; 0 none
	LoadWidth int      // row_load_width=<n>: lanes while the load is above LoadMax; DefaultLoadWidth when unset
	PauseOn   string   // row_pause_on=funds|any: funds (the default) holds only for out of funds; any holds for a rate limit too
	RefuseGo  bool     // row_refuse_go=1: the lane's PATH carries go and gofmt that refuse
}

LaneRules is what a friend's row says about which cards her lanes take and how hard they run: the zero value is no filter, no cap, no load rule, and a rate limit that backs off.

func LaneRulesOf

func LaneRulesOf(answer string) LaneRules

LaneRulesOf reads the lane rules off the friend's beat answer; a word it does not know is ignored and a value that does not parse leaves the field unset.

func (LaneRules) Judge

func (r LaneRules) Judge(id, stream, tier string) (LaneVerdict, string)

Judge is the card filter: a card of a tier outside Tiers (an unknown tier is outside) is taken back; a card whose stream and id match none of Streams is skipped; any other runs. The reason is a sentence for the record and for friend take.

func (LaneRules) LaneWidthUnderLoad

func (r LaneRules) LaneWidthUnderLoad(width int, load float64) (n int, held bool)

LaneWidthUnderLoad is how many lanes may start with the machine's one-minute load at load: the row's width, held to LoadWidth while the load is above the row's LoadMax (a width at or below LoadWidth is never raised). held says it was lowered.

func (LaneRules) Over

func (r LaneRules) Over(row LaneRules) LaneRules

Over is r with every field row sets in place of its own: the flags give the defaults and the friend row, as her beat answers it, wins.

func (LaneRules) OverTokenCap

func (r LaneRules) OverTokenCap(tokens int64) bool

OverTokenCap says a card has spent its cap; a rule with no cap is never over.

func (LaneRules) ProviderStop

func (r LaneRules) ProviderStop(err error) (message string, stop bool)

ProviderStop is the provider failure that stops every lane, and the message to hold the friend down with, as the provider said it: out of funds (402) always; a rate limit (429) only when the row says pause_on=any (otherwise it backs off, rate-limit-backs-off-not-down).

type LaneState

type LaneState struct {
	Sessions map[int]string     `json:"sessions"`
	Epoch    uint64             `json:"epoch,omitempty"`
	GivenUp  []string           `json:"given_up,omitempty"`
	Started  map[string]Started `json:"started,omitempty"`
	// StopReturns is every card the machine's stop took out of a lane and the stop-return
	// owed or taken for it (stop.go): the next loop pass sends what is owed first.
	StopReturns []StopReturn `json:"stop_returns,omitempty"`
}

LaneState is the running daemon's in-memory lane connections and unfinished outcomes. It never decides what a lane holds: the sprint server owns those records (tla/FriendLaneClient.tla). Sessions are reused during this daemon's life; an epoch change forgets them so a stale session cannot cross a clear (epoch.go). No lanes.json file restores this cache after restart.

type LaneTokens

type LaneTokens struct {
	cardcost.Tokens
	USD      string // opencode's own reported cost, a decimal; "" unknown
	Sessions int
}

LaneTokens is what a session and its children spent, as opencode's own database says it, and what opencode itself priced it at.

func TokensFromOpenCode

func TokensFromOpenCode(ctx context.Context, run Exec, db, session string) (LaneTokens, error)

TokensFromOpenCode reads a session's tokens (and its children's) from opencode's own database through the sqlite3 CLI, read only (the tree has no sqlite driver).

func TokensFromOpenCodeIn added in v1.2.7

func TokensFromOpenCodeIn(ctx context.Context, run Exec, dbs []string, session string) (LaneTokens, error)

TokensFromOpenCodeIn reads a session's tokens from the first of dbs that holds it (a row for the session or a child). A lane's opencode runs inside the wall with the wall's HOME (the friend's config directory, else her working directory: sandbox.LaneProfile), so it writes its sessions under that HOME, not the daemon's; a batch turn writes under the daemon's (--db). A session in none of them is ErrSessionInNoDB, naming each database and why, never the all-zero tokens the sums give for no row (2026-10-10: every walled opencode lane read zero tokens from the daemon's database, and its runs were judged empty).

func TokensOf

func TokensOf(out string) (LaneTokens, error)

TokensOf reads the one row TokensSQL prints (sqlite3's default, | between columns).

func (LaneTokens) Sub

func (t LaneTokens) Sub(base LaneTokens) LaneTokens

Sub is the tokens spent after base: a lane's session serves many cards, so a card's are the session's totals at its end less its totals at its start.

type LaneTurn

type LaneTurn struct {
	Exit     int
	Rejected string // the line of the output where the harness refused a permission, if any
	// Windows is the subscription windows' use the harness reported in the turn
	// (a Claude Code run's rate_limit_event lines, ReadRateLimitEvents); nil when
	// it reported none. The lanes are paced by it (pacing.go).
	Windows []WindowUse
	// FirstError is the first line of the turn's output that says an error
	// (HarnessFirstError), "" when none does: a run that exits 0 with no report
	// is a harness fault said with it (lanes.go, faultTurn).
	FirstError string
}

LaneTurn is how one lane turn ended.

type LaneVerdict

type LaneVerdict int

LaneVerdict is what the rules say of a dealt card.

const (
	LaneRun  LaneVerdict = iota // the lanes may run it
	LaneSkip                    // not hers to run: left on her row, never started
	LaneTake                    // outside her tiers: taken back for the dealer (friend take)
)

type Launchctl

type Launchctl func(ctx context.Context, args ...string) (output string, err error)

Launchctl runs launchctl with args and answers what it printed; the installer passes the real one, a test its own.

type Limit

type Limit struct {
	Limited bool
	Until   time.Time
	Reason  string
	Usage   Usage
	Overage bool
}

Limit is what a command's output says of the harness's limit: Limited, until when and why; Usage when the output measured it; Overage when the harness is spending paid overage (a limit only if the owner does not allow it, Limits.AllowOverage).

func ReadLimit

func ReadLimit(out string, now time.Time) (lim Limit, found bool)

ReadLimit reads a command's output at now for the harness's limit: the last rate_limit_event anywhere in it (Claude Code), else a limit line in its tail with the reset beside it (a clock time, today or else tomorrow in now's zone or the zone it names, on the date it names; "in N hours"; an epoch after "limit reached|"; resetOfText). found is whether the output said anything of the limit at all. A provider's transient rate_limit_error is no limit (ProviderRefusal passes it).

func ReadLimitFile

func ReadLimitFile(stateDir string) (l Limit, found bool, err error)

ReadLimitFile reads the limit file; found is false when there is none.

type LimitHit

type LimitHit struct {
	Kind   string
	Until  time.Time
	Named  bool
	Reason string
}

LimitHit is a limit read from a failed turn's output: its kind, the reset (the text's own when Named, else now plus the rest), and the line.

func ParseLimit

func ParseLimit(harness, out string, now time.Time, rest time.Duration) (LimitHit, bool)

ParseLimit reads the tail of a failed turn's output (the last LimitTail bytes: a harness says it last) for harness's own wording of a usage limit or an empty balance: the kind, and the reset when the line names one, else now plus rest (DefaultLimitWait when rest is zero). A harness it has no words for, or a text it does not recognise, is not a limit. A reset that is not after now is no reset. The newest matching line wins.

type Limits

type Limits struct {
	Now  func() time.Time
	Down func(until time.Time, reason string)
	Up   func()
	// Notice is the single coordinator message for a detected limit episode.
	// unread asks it to include the unread-reset judgment in that message.
	// A funds refusal delegates its notification to Funds when configured.
	Notice func(until time.Time, reason string, unread bool)
	// Unread is the judgment when a limit's text names no reset this reads: the
	// friend is held for Rest and the text goes to the coordinator, once until
	// a turn runs clean, so the reset is set by a person (friend down --until)
	// and not guessed again each Rest (usage-limit-reset-read-from-the-message-b.w1).
	Unread func(text string)
	// Funds, when set, is handed a failed turn's own wording of a provider
	// refusal for funds with no reset to wake at (an insufficient balance, a
	// 402): the reason (ratelimit.ProviderLimit's rule, one layer up). The
	// daemon wires it to the spend cap, so the refusal stops the turns at once
	// with the spend-cap judgment instead of the limit's own Rest retry
	// (docs/SPEC-FRIEND.md, spend caps).
	Funds func(reason string)
	// Harness is the harness whose own wording a failed turn is read in
	// (ParseLimit), and Rest how long it is down when the text names no
	// reset (DefaultLimitWait when zero): --limit-rest.
	Harness string
	Rest    time.Duration
	// AllowOverage is the owner's word that this friend may spend paid
	// overage; without it a harness on overage reads down.
	AllowOverage bool
	// Pacing is the row's pacing (the fraction of each subscription window the
	// sprint may spend), read at each batch turn; nil, or out of (0, 1], is
	// DefaultPacing. Every output's rate_limit_event feeds the pacer, and a batch
	// turn while a window is at the pacing is Deferred until it resets
	// (pacing.go).
	Pacing func() float64
	// contains filtered or unexported fields
}

Limits is one friend's harness limit: up, or down until a reset, which lapses at the reset (tla/FriendLimit.tla is owed). Watch reads every command's output for it; Gate holds turns while it is down. The daemon pushes nothing into her session to wake it: the next lane turn after the reset is the test, and a turn that hits the limit again is down again. The hooks say the change to the sprint: Down once per limit with its reset (friend down --until), Up when the reset passed.

func (*Limits) Beat

func (l *Limits) Beat(beat func(ctx context.Context) error) func(ctx context.Context) error

Beat is beat held back while the harness is at its limit: no beat goes to the sprint server, so her row reads down, from the turn that hit the limit until its reset.

func (*Limits) BeatOrDown

func (l *Limits) BeatOrDown(beat func(ctx context.Context) error, down func(ctx context.Context, until time.Time, reason string) error) func(ctx context.Context) error

BeatOrDown is beat while the harness answers, and down while it is at its limit (limits-mean-down-w-r5.w1~15): her beat says down with the until and the reason (nova-sprint friend beat --until --reason), so her row reads down and why rather than going silent, until the reset. A nil down holds the beat back as Beat does; a down the sprint server refuses is an error, and her row reads down by the lapse as before.

func (*Limits) Gate

func (l *Limits) Gate(d Deliverer) Deliverer

Gate is d held by the limit: a delivery while the harness is down is Deferred without running it; a turn that hits a limit is Deferred too. A passive d is d. A LaneHarness stays one (gatedLanes): its batch Deliver is held, and its lanes are not. docs/SPEC-FRIEND.md, silent-turn stop and refusals

func (*Limits) Kind

func (l *Limits) Kind() string

Kind is what the limit is, KindLimit or KindCredits, while there is one.

func (*Limits) Limited

func (l *Limits) Limited() (until time.Time, reason string, limited bool)

Limited is the limit now: until when and why, and whether there is one. A limit whose reset has passed lapses here, said once by Up.

func (*Limits) Refuse

func (l *Limits) Refuse(kind, reason string, until time.Time)

Refuse is a credit or quota refusal a caller read where this harness's own wording parse (ParseLimit) and the limit tail found none: the daemon's per-harness table in cmd/nova-friend/limit.go reads a lane's evidence and hands the hit here (docs/SPEC-FRIEND.md, every harness's credit and quota refusal). It takes the same state and hooks see takes for a limit a harness's own wording names: the friend is down until the refusal's until, her turns and beats are held, Down says it to the sprint once, and status reads session=limited limit_kind=kind limit_until=until. A refusal equal to the one that already holds her is not a second down.

func (*Limits) Watch

func (l *Limits) Watch(run Exec) Exec

Watch is run reading every command's output for a limit.

func (*Limits) WindowUse

func (l *Limits) WindowUse() string

WindowUse is the subscription windows' use as the harness last reported it in any command's output ("5h 62% 7d 31%"), empty when none is live.

type LogFacts

type LogFacts struct {
	Last           string // RFC3339 of the newest delivery in the window, or "-"
	LastExit       string // its exit, or "-"
	FailedOfLast20 int    // failures among the newest 20 deliveries in the window
	Deferred       int    // deferrals in the window
	Delivered      int    // deliveries in the window
	Failed         int    // of those, the ones that exited non-zero
}

LogFacts is what the daemon's log says inside a window.

func ParseLog

func ParseLog(lines []string, from time.Time) LogFacts

ParseLog reads the daemon's log lines for deliveries and deferrals at or after from; a line with no RFC3339 stamp is outside every window (docs/SPEC-FRIEND.md "Check": the facts).

type NoReport

type NoReport struct {
	Run    string // the harness as the run names it, "claude -p"
	Exit   int
	Outbox string
	Lacks  []string // the files the outbox lacks
}

NoReport is a card runner's run that ended with its outbox holding neither REPORT.md nor RESULT.md (the Claude lane's), its words as the run says them.

func (NoReport) Error

func (e NoReport) Error() string

type NotServed

type NotServed struct{ Why string }

NotServed is a sprint server that refused friend cards (Refused) where the daemon has no worker view to fall back on; it is said once a ServedEvery, each time the verb is asked again.

func (*NotServed) Error

func (n *NotServed) Error() string

type NotStageable

type NotStageable struct {
	Repo, Card, Why, Remedy string
	Job                     string // the job it was staging, when there was one
}

NotStageable is a stage that waits on a person: her account cannot reach the repository (Card empty: every card on it waits), or the card's packet names what is not there. It is one judgment to the coordinator, its Remedy what clears it.

func (*NotStageable) Error

func (n *NotStageable) Error() string

type Notices added in v1.3.16

type Notices struct {
	Bus    *bus.Bus
	To     func(context.Context) (string, error)
	Record func(string)
	Now    func() time.Time           // nil uses the monotonic process clock
	Wait   func(context.Context) bool // nil waits one second; false means cancelled
	// contains filtered or unexported fields
}

Notices retains coordinator messages until the bus returns a receipt. Each message keeps its token, including after a lost response, so retry cannot duplicate a message. The queue is volatile: a shutdown reports its unsent count. A full queue holds its caller until delivery frees space or shutdown cancels the call; the independent metadata beat continues. Libraries considered: bus.Message.Token supplies idempotency; sync.Mutex serializes the lane-output callbacks and the daemon's retry step.

func (*Notices) Flush added in v1.3.16

func (n *Notices) Flush(ctx context.Context)

Flush retries each pending message (FriendNotices.Send). A refused message cannot block independent episodes. Retries stop before the bus token can expire; an unknown old receipt stays visible for manual resolution. The target is fixed before the first send, so a retry cannot change a token's arguments.

func (*Notices) Send added in v1.3.16

func (n *Notices) Send(ctx context.Context, m bus.Message) bool

Send records one episode's message and tries delivery immediately. A full queue applies backpressure without losing the caller's event (FriendNotices.Queue). False means shutdown cancelled admission; the unsent subject is recorded.

func (*Notices) Unsent added in v1.3.16

func (n *Notices) Unsent() int

Unsent is the outstanding count, reported at shutdown because this queue deliberately carries no second persistent store of the bus's receipts.

type OSFS

type OSFS struct{}

OSFS is the real filesystem; a write is atomicfile's.

func (OSFS) Lstat

func (OSFS) Lstat(p string) (Entry, error)

func (OSFS) MkdirAll

func (OSFS) MkdirAll(p string, perm fs.FileMode) error

func (OSFS) ReadFile

func (OSFS) ReadFile(p string) ([]byte, error)

func (OSFS) WriteFile

func (OSFS) WriteFile(p string, data []byte, perm fs.FileMode) error

type OpenCode

type OpenCode struct {
	Dir, Session string
	Run          Exec
	Program      string    // "opencode" when empty
	Out          io.Writer // where the turn's output goes, when set: the daemon's record
	// Allow is every other path the friend's directory is reached by (a
	// symlink in the home directory): with Dir and its real path, allowed in
	// the project config before a turn (AllowDirs), so a headless run never
	// auto-rejects a tool call there. Nil: the config is left alone.
	Allow []string
	// Standalone passes --standalone to every run: opencode 2.0.25 (the
	// background service, 2026-10-09) makes `opencode run` attach to a shared
	// background service (`opencode serve --service`, started once, detached)
	// instead of a private server, so a sealed provider key (nova-secrets exec)
	// reaches no provider ("Incorrect API key provided"); --standalone keeps the
	// run in this process, with this environment. CheckRun sets it when the
	// installed run lists the flag, so an older opencode is never handed it.
	Standalone bool
}

OpenCode delivers through `opencode run --session <id> <text>` run with Dir as its working directory (the process's, never a flag: opencode v2.0.20's run has no --dir, and every delivery that passed one exited 1, "Unrecognized flag: --dir", 2026-10-06), which blocks for the whole turn; without a session named, the newest session whose directory is Dir, from `opencode session list --format json`, so a friend who starts a fresh session is still reached. CheckRun, at the daemon's start, refuses an opencode whose run lacks a flag the adapter passes.

func (*OpenCode) CheckRun

func (o *OpenCode) CheckRun(ctx context.Context) error

CheckRun reads the installed opencode once, at the daemon's start: its version (`opencode --version`) and its run verb's flags (`opencode run --help`), and is a one-line refusal naming the version when the help lacks a flag the adapter passes (OpenCodeRunFlags), so the next change of the CLI is a named refusal and not a silent exit 1 on every delivery (the finding of 2026-10-06: --dir, gone from run in v2.0.20). A help that lists no flag at all, or that cannot be read, cannot tell, and is nil: the deliveries say what they meet.

func (*OpenCode) Deliver

func (o *OpenCode) Deliver(ctx context.Context, text string) (int, error)

func (*OpenCode) DeliverFresh added in v1.2.7

func (o *OpenCode) DeliverFresh(ctx context.Context, text string) (int, error)

DeliverFresh is one fresh turn of the friend's opencode, a session of its own that no listing is read for and none is grown: `opencode run <text>` in Dir with no --session. It is the daemon's presence check for a one-shot friend (docs/SPEC-FRIEND.md, presence: a tiny fresh turn per check; FriendPresence.tla, OneShotNeverDownBySession), so a session that is full or missing is never the reason she is down. Its output is read as a lane turn's is.

func (*OpenCode) DeliverTo

func (o *OpenCode) DeliverTo(ctx context.Context, id, text string) (LaneTurn, error)

DeliverTo is one card's turn in a lane's session: `opencode run --session <id> <text>` in Dir, its output read for a refused permission, and its tail for a rate limit or out of funds (ProviderLimit, whatever the exit: the lanes heed it only when the card has no RESULT.md) before a provider's refusal of the session.

A lane's turn runs in the card's job directory, the one its context carries (WithLaneDir), so a path the model reads relative is read inside the job; with none it runs in Dir.

func (*OpenCode) OpenSession

func (o *OpenCode) OpenSession(ctx context.Context, seed string) (string, error)

OpenSession runs the seed as the first turn of a new session in Dir (`opencode run <seed>` in Dir, no --session) and answers the session that appeared in the listing of Dir, the newest the listing before it did not hold. The lanes' directories are allowed in the project config first.

func (*OpenCode) RunRead

func (o *OpenCode) RunRead(ctx context.Context, model, prompt string) (LaneTurn, error)

RunRead is one read as a one-shot of the friend's opencode: `opencode run [--model <model>] <prompt>` in Dir, a session of its own that no listing is read for, so reads never queue behind the lanes' session opens. Its output is read as a lane turn's is.

type OpenCodePriced

type OpenCodePriced struct {
	*OpenCode

	// Friend is the friend's name, as a capped card's report says it.
	Friend string
	// TokenCap is the friend row's per-card token cap as the daemon last read
	// it (TokenCapOf; 0 none); nil is DefaultTokenCap.
	TokenCap func() int64
	// Tick is the clock a running turn's usage is polled on (every TokenPoll);
	// a real ticker when nil.
	Tick func(time.Duration) (<-chan time.Time, func())
	// contains filtered or unexported fields
}

OpenCodePriced is an OpenCode friend's lanes with every run priced from opencode's own session record (docs/SPEC-FRIEND.md, the Claude lanes, the OpenCode lane): after each turn `opencode export <session>` is read, and the run's cost is what the session's assistant messages gained since the last read (each message's cost is opencode's own figure), said on the record as one line per run. The daemon wraps the friend's OpenCode in it; a bare OpenCode runs no export. A card's turn is capped by its tokens, read from the same record (tokencap.go).

func (*OpenCodePriced) DeliverTo

func (p *OpenCodePriced) DeliverTo(ctx context.Context, id, text string) (LaneTurn, error)

DeliverTo is OpenCode's under the card's token cap (deliverCapped), then the run priced, whatever it answered.

func (*OpenCodePriced) OpenSession

func (p *OpenCodePriced) OpenSession(ctx context.Context, seed string) (string, error)

OpenSession is OpenCode's, then the first run priced.

func (*OpenCodePriced) SpendLine

func (p *OpenCodePriced) SpendLine() string

SpendLine is every run's cost so far and, when the last run stopped at a usage limit, its reset: `spend: harness=opencode runs=<n> cost_usd=<sum> [limited_until=<t>]`. An API friend has no five-hour or weekly window to read; her limit is what the run said (Spender).

func (*OpenCodePriced) Spent

func (p *OpenCodePriced) Spent() float64

Spent is the cost of every run priced so far, in US dollars.

type OutOfFunds

type OutOfFunds struct{ Session, Reason string }

OutOfFunds is a lane's answer when the provider refused for want of funds or credit (a 402, insufficient balance): the lanes hold until the daemon restarts, and the coordinator is told once (LaneGovernor.Hold).

func (OutOfFunds) Error

func (o OutOfFunds) Error() string

type Pacer

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

Pacer is a friend's lanes paced by her subscription windows. Its zero value knows no window and paces nothing.

func (*Pacer) Observe

func (p *Pacer) Observe(uses []WindowUse)

Observe takes a turn's report: each window replaces its last report. A report with no utilization (under the harness's warning threshold) keeps the use last read for the same reset, since use does not fall within a window.

func (*Pacer) Step

func (p *Pacer) Step(now time.Time, width int, pacing float64) (paced int, line string, judge bool)

Step is the pacer at now: the effective width, the line that says a change of it (empty when none), and whether this step owes the coordinator the judgment (paced below half her row, said once until she is back at half or more).

func (*Pacer) Use

func (p *Pacer) Use(now time.Time) string

Use is the live windows' use as the table says it: "5h 62% 7d 31%", empty when none is live.

func (*Pacer) Width

func (p *Pacer) Width(width int, pacing float64, now time.Time) (int, string)

Width is the lanes' effective width at now for the row's width and the pacing fraction, and the window that decided it ("" when none did). A window at or past the pacing, or that the harness rejected, allows none until it resets; below it, the width is the row's scaled by the share of the paced budget left, rounded up: the lanes thin as the window fills.

type Packet

type Packet struct {
	Card, Job, Repo, Base, BaseSha, Branch string
	Attempt                                int
}

Packet is what a held work card says about its checkout: the repository (owner/name), the base it starts at (a branch, a tag or a full sha) and its pin (BaseSha, the commit a `BASE: <ref>@<sha40>` names; "" when unpinned), the branch its work is pushed to, and its attempt.

func PacketOf

func PacketOf(h HeldCard) (Packet, bool)

PacketOf is the packet of a held card, the server's fields first and the brief's lines (its STATUS line, REPO: and BASE:) for any it did not send. It answers false for a card that stages nothing here: a read, or a work card whose brief names no REPO.

type Pronouns added in v1.2.16

type Pronouns string

Pronouns are the pronouns a friend goes by: the friend row's `pronouns` setting (pkg/config/kind.go, "friend"), one of the three sets below. Pronouns are read, never inferred: a friend whose pronouns are not stated goes by they/them, and no text a daemon writes calls a friend "it". The zero value is they/them, so a Daemon or a text built without them is already right.

const (
	SheHer   Pronouns = "she/her"
	HeHim    Pronouns = "he/him"
	TheyThem Pronouns = "they/them"

	DefaultPronouns = TheyThem
)

The pronoun sets a friend row may name, and the default a row without one has. pkg/config's FriendPronounSets lists the same three (TestPronounSetsMatchConfig holds the two lists equal).

func ParsePronouns added in v1.2.16

func ParsePronouns(s string) (Pronouns, bool)

ParsePronouns is the set a row's or a flag's text names: "" is the default, the three sets are themselves, and anything else is false with the default, never a guess.

func RowPronouns added in v1.2.16

func RowPronouns(answer string) (Pronouns, bool)

RowPronouns reads the friend row's pronouns off the beat's answer (row_pronouns=she/her, beside row_mode and row_width); ok is false when the answer carries none or names a set that is not one of the three.

func (Pronouns) Say added in v1.2.16

func (p Pronouns) Say(s string) string

Say is the one helper every text about a friend renders its pronouns with: s with {they}, {them}, {their}, {theirs} and {themself} (and {They}, {Them}, {Their}, {Theirs}, {Themself} at the head of a sentence) replaced by the forms of p. A text is written once in terms of those words, and a verb that would change with the subject ("she beats", "they beat") is not written after one: the text names the friend, or has the possessive ("{their} beat says down").

func (Pronouns) Sprintf added in v1.2.16

func (p Pronouns) Sprintf(format string, a ...any) string

Sprintf is fmt.Sprintf of the format with its pronoun words said for p (Say), so a text that carries {their} and the like is written once.

type PronounsRef added in v1.2.16

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

PronounsRef holds a daemon's pronouns where several goroutines read them and a beat's answer may change them: the flag's (or the plist's) first, the row's once the beat names them. A nil ref and a ref never set are they/them.

func NewPronounsRef added in v1.2.16

func NewPronounsRef(p Pronouns) *PronounsRef

NewPronounsRef is a ref holding p.

func (*PronounsRef) Get added in v1.2.16

func (r *PronounsRef) Get() Pronouns

Get is the pronouns held, they/them when none were set.

func (*PronounsRef) Set added in v1.2.16

func (r *PronounsRef) Set(p Pronouns)

Set replaces the pronouns held.

type ProviderRefused

type ProviderRefused struct{ Session, Reason string }

ProviderRefused is a Deliverer's answer when the turn reached the session's model provider and the provider refused the request itself (an invalid_request_error, an authentication_error): the session is at fault, not the message. The daemon counts it toward nothing a message owns; the same refusal on BrokenAfter turns in a row marks the session broken (the finding of 2026-10-04: a friend's session refused every turn for two hours and nothing said so).

func (ProviderRefused) Error

func (p ProviderRefused) Error() string

type Queue

type Queue struct {
	Tasks []Task `json:"tasks"`
}

Queue is the friend's queue file: one record per task; the coordinator writes assignments into it, the session marks them working or done.

func (Queue) Counts

func (q Queue) Counts() (queue, working int)

Counts is what the queue file says: tasks queued and tasks working.

type RateLimited

type RateLimited struct{ Session, Reason string }

RateLimited is a lane's answer when the provider rate-limited the turn (a 429, "rate limit reached", "too many requests", "input token limit exceeded"): the turn's card stays in hand, counted toward nothing, and the lanes back off (LaneGovernor.RateLimit).

func (RateLimited) Error

func (r RateLimited) Error() string

type ReadHarness

type ReadHarness interface {
	RunRead(ctx context.Context, model, prompt string) (LaneTurn, error)
}

ReadHarness is a harness that runs one read as a one-shot of its own: a new session with the prompt as its only turn, on model ("" is the harness's own), blocking until it ends. A harness without it runs the prompt as the seed of a new lane session (LaneHarness).

type ReadPacket

type ReadPacket struct {
	Tier       string `json:"tier"`
	Head       string `json:"head"`
	WorkBranch string `json:"work_branch"`
	Attempt    int    `json:"attempt"`
	Brief      string `json:"brief"`
	Report     string `json:"report"`
}

ReadPacket is what the reader queue says of one asked read.

type Refused

type Refused struct{ Why string }

Refused is the sprint server's refusal of a verb it answered (a non-zero exit), as against a server that did not answer: a refused friend cards is a server that does not serve it yet.

func (*Refused) Error

func (r *Refused) Error() string

type RoutePrice

type RoutePrice struct {
	Name   string
	Prices cardcost.Prices
	Found  bool
}

RoutePrice is the store's route row for the friend's exact provider/model: its name and price sheet; Found is false when there is none.

func RoutePriceOf

func RoutePriceOf(routesJSON, provider, model string) RoutePrice

RoutePriceOf finds the route row for provider/model in the sprint's `routes --json` answer: the first object anywhere in it that has a prices object and this provider and model. A route that does not say reasoning_as_output bills reasoning as output.

type Row

type Row struct {
	Cards []HeldCard
	Reads bool
	From  string
	Note  string
	Epoch uint64
	// Refused is why the server refused friend cards this pass (a refusal it
	// answered, Refused); "" when it answered, or was not asked this pass. The
	// daemon counts these per verb and raises the third consecutive one
	// (inboxStep; docs/SPEC-FRIEND.md, "A refused verb").
	Refused string
}

Row is one answer of what is on her row: the cards, whether her reads are among them (a job no card names is retired only when the answer covers its kind: the worker view lists her work cards and none of her reads), where the answer came from, and Note, a line the daemon says with it ("" for none: the server's refusal of friend cards, once a ServedEvery). Epoch is the sprint's epoch as the answer said it: the highest on her row, every card on her row being dealt at the sprint's current epoch, and on a view answer the view's own; zero when the answer names no card and the epoch is unknown (an empty row: nothing is retired on it, and the next deal names the new one).

type SessionRefused

type SessionRefused struct{ Session, Reason, Detail, Remedy string }

SessionRefused means the harness cannot take the turn, whatever its exit code. The delivery check and reach surface its reason and remedy.

func DSHRefusal

func DSHRefusal(session, dir, out string) (SessionRefused, bool)

DSHRefusal reads one headless turn's output, whatever its exit code, for an answer that the session cannot take a turn at all: the agent preset refusal, or MISSING_CREDENTIAL. Either is a failed delivery (SessionRefused), never a delivered turn: the finding of 2026-10-06, Zhi's session under preset "minimal" printed the refusal and exited 0, and four hours of messages counted as delivered while her row read up. The reason is fixed text; the output itself, which may name a credential, is never carried.

func (SessionRefused) As

func (s SessionRefused) As(target any) bool

As answers a SessionRefused as the Deferred it also is: the message stays pending, and a session it cannot drive carries its Remedy.

func (SessionRefused) Error

func (s SessionRefused) Error() string

type Setting

type Setting struct {
	Harness, File, Name, Want, Have string
}

Setting is one setting a harness needs: the file (or directory) it lives in, its name, the value install writes and the value there now. A setting whose Have is not its Want has drifted.

func Drift

func Drift(all []Setting) []Setting

Drift is the settings of a Check that differ from what install writes.

func (Setting) Drifted

func (s Setting) Drifted() bool

Drifted says whether what is there is not what install would write.

type SettingsFS

type SettingsFS interface {
	Lstat(path string) (Entry, error)
	ReadFile(path string) ([]byte, error)
	WriteFile(path string, data []byte, perm fs.FileMode) error // whole, atomically
	MkdirAll(path string, perm fs.FileMode) error
}

SettingsFS is the filesystem the settings are read and written through: OSFS for install and check, friendtest.MemFS (the in-memory twin) for a test.

type ShownEntry

type ShownEntry struct {
	State   string `json:"state"`
	Working int    `json:"working"`
}

ShownEntry is the shown status of a friend (passed via --shown).

func LookupShown

func LookupShown(shown map[string]ShownEntry, friend string) *ShownEntry

LookupShown returns the ShownEntry for friend, checking friend name then wildcard.

type SpendCap added in v1.2.14

type SpendCap struct {
	Friend   string
	Provider string
	// Now is the daemon's clock (a fake in tests).
	Now func() time.Time
	// DayStart is the start of the spend day containing now; when nil the day
	// resets at UTC midnight.
	DayStart func(now time.Time) time.Time
	// Cap is the day's spend cap in USD for this provider; nil, or not
	// positive, is DefaultSpendCapUSD.
	Cap func() float64
	// Spent is today's spend in USD as the harness's own usage report says it
	// for the day that starts at dayStart. It is read before every turn launch.
	Spent func(dayStart time.Time) float64
	// Judgment is the one judgment raised to the seat and the owner when the cap
	// is passed; it returns the line naming the friend, the provider and the
	// spend. It is called once per spend day until the day resets, and once on a
	// provider refusal for funds.
	Judgment func(dayStart time.Time, spent, cap float64) string
	// Idle is whether the friend has no dealt work and no live session; when it
	// is set and tells, no turn launches at all, with no judgment (docs/SPEC-FRIEND.md,
	// idle). It is checked before the spend cap.
	Idle func() bool
	// Halt, when set, is called when the cap is passed or the provider refused
	// for funds, with the reason and the instant the stop ends: cmd/nova-friend
	// wires it to the harness's own limit gate (Limits.Refuse), so every turn
	// stops, the session liveness check included, not only the ones this gate
	// wraps. Nil: this gate's deliveries alone are stopped.
	Halt func(reason string, until time.Time)
	// contains filtered or unexported fields
}

SpendCap is a friend's spend cap per day and per provider (docs/SPEC-FRIEND.md, spend caps). A friend daemon has a cap for each provider, read from its config, $20 a day by default. Spend is measured from the harness's own usage report, never estimated. At the cap the daemon stops launching turns and raises one judgment to the seat and the owner naming the friend, the provider and the spend; a provider refusal for funds (an insufficient balance, a 402) stops the turns at once with the same judgment rather than retrying hourly.

func (*SpendCap) Check added in v1.2.14

func (s *SpendCap) Check(now time.Time)

Check is the daemon's per-step read of the cap, for the steps where no delivery is attempted at all (a friend with nothing dealt): it raises the one judgment and halts the wired gate when the day's spend has passed the cap or the provider refused for funds, exactly as a delivery attempt does (docs/SPEC-FRIEND.md, spend caps). cmd/nova-friend calls it from its row hook, which the daemon's loop runs before it launches any turn.

func (*SpendCap) Gate added in v1.2.14

func (s *SpendCap) Gate(d Deliverer) Deliverer

Gate wraps a Deliverer so that no turn is launched on a day whose measured spend has passed the cap, or after a provider refusal for funds: Deliver answers Deferred, the message kept in hand, and the one judgment for the day is raised. It is used exactly where Limits gates a harness at its limit (limit.go), so a spend-cap day stops launching turns the same way a limit does. A LaneHarness stays one (spendCappedLanes): its batch Deliver is held, and its lanes are not.

func (*SpendCap) Judged added in v1.2.14

func (s *SpendCap) Judged(now time.Time) string

Judged is the judgment raised for the day now, "" when none is.

func (*SpendCap) Refuse added in v1.2.14

func (s *SpendCap) Refuse(reason string)

Refuse marks the daemon stopped by a provider refusal for funds (an insufficient balance, a 402): the same judgment is raised to the seat and the owner, and no turn launches until the daemon restarts (docs/SPEC-FRIEND.md, spend caps). The judgment is the hook's own when it is set, so the refusal tells the seat and the owner exactly as a spent-out day does.

type Spender

type Spender interface {
	SpendLine() string
}

Spender is a lane harness that prices its runs (docs/SPEC-FRIEND.md, the Claude lanes, on the beat): SpendLine is what its runs have cost so far and the limit it last read, one line the daemon says on its beat when it changed; empty before any run.

type Stager

type Stager struct {
	Dir string
	URL func(repo string) string
	Env []string
	// contains filtered or unexported fields
}

Stager stages jobs under Dir, her working directory, with the git credentials of the process that runs it (the daemon's: her account's). URL names a repository's remote (nil: GitHubURL); Env is git's whole environment (nil: the daemon's own, with GIT_TERMINAL_PROMPT=0, so git never waits on a prompt no one answers).

func (*Stager) Prune

func (s *Stager) Prune(ctx context.Context, live map[string]bool, kept int) ([]string, error)

Prune removes the worktrees of finished jobs past kept, the oldest staged first (by its JOB.md), at most PrunePerPass of them, and answers the jobs it removed; a job whose mirror a stage holds is left for the next pass, never waited on. A job is finished when it is not live (held on her row, run by a lane, being staged: the caller's live) and its brief is not in her inbox (the inbox cleanup retired it); only a job whose checkout is a worktree of one of her mirrors is ever pruned, never a clone or anything another hand staged. A pruned job is gone whole (jobs/<job>), its worktree removed from the mirror; its branch stays in the mirror, so any commit on it is kept, and a stage of the job again takes the branch as it stands.

func (*Stager) Stage

func (s *Stager) Stage(ctx context.Context, p Packet) (string, error)

Stage stages p's job: the mirror of its repository fetched (cloned the first time), a git worktree of it at the base on the card's branch at jobs/<job>/repo, whose origin is the repository itself (the mirror's origin), and jobs/<job>/JOB.md. It answers the commit the checkout is at. A job already staged is left as it is; a checkout is never half there (the worktree is added beside, under jobs/<job>/.staging, and moved in whole), and JOB.md is written last, so a lane never meets a checkout not ready. A branch the mirror already holds (a pruned job's, staged again) is checked out as it stands, never reset to the base.

func (*Stager) Tip

func (s *Stager) Tip(ctx context.Context, repo, branch string) (string, error)

Tip is origin's tip of branch in repo (owner/name): one git ls-remote of the one ref, bounded by TipBudget; "" when origin has no such branch.

type Started

type Started struct {
	Lane int       `json:"lane"`
	Card Card      `json:"card"`
	At   time.Time `json:"at"`
}

Started is a card a lane began, kept in the lane state until the lane is done with it: a daemon that starts up and finds one finishes it, for the run that held it is gone.

type Status

type Status struct {
	Friend  string    `json:"friend"`
	Harness string    `json:"harness"`
	Started time.Time `json:"started"`
	At      time.Time `json:"at"` // when this was written
	// Seat is the coordinator seat holder as the sprint server last said it: who
	// the daemon tells. Whether the friend is there is the nova-hook record's
	// (nova-friend status and check read it), never this file's.
	Seat       string    `json:"seat"`
	Beats      int       `json:"beats"`
	LastBeat   time.Time `json:"last_beat"`
	BeatError  string    `json:"beat_error,omitempty"`
	StoreError string    `json:"store_error,omitempty"`
	Width      int       `json:"width"`
	// Session is SessionOK, or SessionBroken once the provider refused BrokenAfter
	// turns in a row the same way; empty for a passive harness.
	Session       string    `json:"session,omitempty"`
	SessionID     string    `json:"session_id,omitempty"`
	SessionReason string    `json:"session_reason,omitempty"`
	BrokenAt      time.Time `json:"broken_at,omitempty"`
	// While the harness is at its usage limit or out of credits Session is
	// SessionLimited, LimitKind says which (KindLimit or KindCredits) and
	// LimitUntil is the reset (docs/SPEC-FRIEND.md, limits-mean-down-w-r.w1~15).
	LimitKind  string    `json:"limit_kind,omitempty"`
	LimitUntil time.Time `json:"limit_until,omitzero"`
	// Mode is how the daemon delivers now (batch or one-shot), and Lanes the
	// one-shot lanes as n:session:card/turn, empty in batch.
	Mode  string `json:"mode,omitempty"`
	Lanes string `json:"lanes,omitempty"`
	// Paced is the lanes' effective width under the subscription windows' pacing
	// (pacing.go), nil before the lanes have stepped; Window is the windows' use
	// as the harness last reported it ("5h 62% 7d 31%", empty when none is live),
	// and Pacing the row's pacing as a percent.
	Paced  *int   `json:"paced,omitempty"`
	Window string `json:"window,omitempty"`
	Pacing string `json:"pacing,omitempty"`
	// Held, InboxJobs and Missing are the last inbox reconcile's counts (SyncInbox): the
	// cards on her row, the sprint jobs in her inbox, and the held cards with no BRIEF.md
	// after it; HeldKnown is false until the server has answered once, and InboxError is
	// why the last reconcile did not finish, empty when it did.
	HeldKnown  bool   `json:"held_known,omitempty"`
	Held       int    `json:"held"`
	InboxJobs  int    `json:"inbox"`
	Missing    int    `json:"missing"`
	InboxError string `json:"inbox_error,omitempty"`
	// HeldFrom is where the last answer came from: friend cards, or the worker view while the
	// server does not serve it (no brief is written from the view).
	HeldFrom string `json:"held_from,omitempty"`
	// Refused is the server's refusals counted per verb: each verb's consecutive
	// refusals, cleared when it answers again (docs/SPEC-FRIEND.md, "A refused verb").
	Refused map[string]int `json:"refused,omitempty"`
	// LastRefusal is the last refusal as "verb: why", and when, on the daemon's clock.
	LastRefusal   string    `json:"last_refusal,omitempty"`
	LastRefusalAt time.Time `json:"last_refusal_at,omitzero"`
	// Proof is the harness proof's last result ("ok", or the terminal reason it
	// cannot answer), and ProofAt when it ran.
	Proof   string    `json:"proof,omitempty"`
	ProofAt time.Time `json:"proof_at,omitzero"`
}

Status is what the daemon knows, as status reads it: one file, rewritten whole, so a reader never sees half a state.

func ReadStatus

func ReadStatus(stateDir string) (s Status, found bool, err error)

ReadStatus is what the daemon last wrote; found is false when it never has.

type StopReturn

type StopReturn struct {
	Lane int `json:"lane"`
	// ServerLane is the lane the server says holds the card (take --lane), named on the
	// stop-return so another lane's card is refused; 0 when no lane holds it
	ServerLane int       `json:"server_lane,omitempty"`
	Job        string    `json:"job"`
	Row        string    `json:"row"`  // the owner row the return is sent as: friend.<name>, or the reader row
	Card       string    `json:"card"` // the card id
	Gen        int       `json:"gen"`
	Epoch      string    `json:"epoch"`
	Pid        int       `json:"pid,omitempty"`   // the process signalled, when the harness said it (0: its group, by the lane's context)
	Exit       int       `json:"exit"`            // how the run ended; -1 while it has not
	Ended      bool      `json:"ended"`           // the run has ended: the return may be sent
	At         time.Time `json:"at"`              // when the stop cancelled it
	Tries      int       `json:"tries,omitempty"` // stop-returns sent and refused
	Result     string    `json:"result,omitempty"`
	// NextTry is when a refused stop-return is sent again (StopReturnRetry), and Refusal
	// the refusal last said for it, said once while its text stands.
	NextTry time.Time `json:"next_try,omitzero"`
	Refusal string    `json:"refusal,omitempty"`
}

StopReturn is one card the machine's stop took out of a lane: the evidence (the lane, the job, the card at its generation, the epoch, the process's end) and the stop-return owed for it, Result "" while it is owed and the server's answer once it took it.

func (StopReturn) Owed

func (s StopReturn) Owed() bool

Owed says the stop-return has not been taken by the server.

type Stub

type Stub struct{ Harness, Reason string }

Stub is a harness with no deliver command yet: it refuses every delivery with the way a session of that harness still reads the bus, so the tool is honest. It is Passive: the daemon takes nothing off the stream for it (the session's own blocking read does), only peeks, so a ping is still answered by the daemon at once and the beat is real.

func (Stub) Deliver

func (s Stub) Deliver(context.Context, string) (int, error)

func (Stub) Passive

func (Stub) Passive()

Passive marks a Deliverer that cannot deliver: the daemon reads nothing for it.

type Task

type Task struct {
	Gen int    `json:"gen,omitempty"` // assignment generation; absent means 1 (docs/FRIENDS.md)
	Job string `json:"job,omitempty"` // delivered inbox directory, when recorded

	ID          string `json:"id"`
	State       string `json:"state"` // queued, working, done
	Deliverable string `json:"deliverable,omitempty"`
}

Task is one record of the queue file.

type Tmux

type Tmux struct {
	Dir     string         // the friend's directory, the working directory of each tmux call
	Session string         // the tmux session, friend-<name>
	Prompt  *regexp.Regexp // the idle prompt, matched against the last non-empty line
	Run     Exec
	Out     io.Writer
	Now     func() time.Time                           // time.Now when nil
	Sleep   func(ctx context.Context, d time.Duration) // a real wait when nil
}

Tmux delivers into a TUI hosted in a tmux pane. Deliver captures the pane (`tmux capture-pane -p -t <session>`); when its last non-empty line matches the idle prompt it types the text literally (`send-keys -l`), then Enter as a second call, and is accepted once the prompt line has gone, the turn having started. While the prompt is absent a turn runs: Deferred, so no second turn lands beside one. A missing session is Deferred with the host line, never a failure.

func (*Tmux) Busy

func (t *Tmux) Busy(ctx context.Context) (bool, error)

Busy says whether a turn runs in the pane now: the session exists and its prompt is absent (SPEC-FRIEND.md, "Hosted in tmux"). A missing session is not busy.

func (*Tmux) Deliver

func (t *Tmux) Deliver(ctx context.Context, text string) (int, error)

Deliver types text into the idle pane (SPEC-FRIEND.md, "Hosted in tmux": the pane's prompt shown is the session's free state, and the typed line starts the turn that makes it busy).

type TokenCapped

type TokenCapped struct {
	Cap   int64
	At    Tokens // the card's usage when the lane stopped it
	Card  string
	Wrote bool // the lane wrote the card's REPORT.md (false: hers stood, or none could be written)
}

TokenCapped is a card's turn the lane stopped at its token cap.

func (TokenCapped) Error

func (e TokenCapped) Error() string

type Tokens

type Tokens struct {
	Input, CacheRead, CacheWrite, Output, Reasoning int64
}

Tokens is a card's tokens as the harness's own usage record counts them.

func SessionTokens

func SessionTokens(export string) (Tokens, error)

SessionTokens is a session's tokens from its export: its assistant messages' tokens summed. An export with no message carrying them answers errNoTokenShape, and the cap is not applied; its price (SessionCost) stands as it is.

func (Tokens) String

func (t Tokens) String() string

func (Tokens) Sum

func (t Tokens) Sum() int64

Sum is every token the card was charged for: what the cap counts.

type Unparseable added in v1.3.16

type Unparseable struct {
	Field string // the field the line is missing: gen, kind or job
	Why   string
}

Unparseable is a LANE line that names the lane that asked but that the lane cannot read: a field it needs (gen, kind, job) missing or malformed. It is a Refused too. Asking again gets the same line from the same server, so the lane says it once and asks no more until the daemon restarts (laneAsk), rather than once a TakeRetry forever.

func (*Unparseable) Error added in v1.3.16

func (u *Unparseable) Error() string

func (*Unparseable) Unwrap added in v1.3.16

func (u *Unparseable) Unwrap() error

Unwrap makes an unparseable line a refusal for errors.As.

type Usage

type Usage struct {
	FiveHour, SevenDay             float64
	FiveHourResets, SevenDayResets time.Time
	At                             time.Time
}

Usage is a harness's measured utilization of its five-hour and weekly windows, each a fraction (1 is the window spent), with when each resets and when it was measured (zero: never).

type UsageLimited

type UsageLimited struct {
	Session, Reason string
	Until           time.Time
}

UsageLimited is a lane's answer when the harness account itself is at its usage limit (a Claude Code rate_limit_event rejected, adapter_claude.go): unlike a rate limit it names its reset, so the lanes stop taking until Until (LaneGovernor.PauseUntil) and no cap is lowered; the card stays in the lane's hand, counted toward nothing.

func (UsageLimited) Error

func (u UsageLimited) Error() string

type Verdict

type Verdict struct {
	Status   string   `json:"status"` // up or down
	Reason   string   `json:"reason"`
	Evidence []string `json:"evidence"`
}

Verdict is a friend's status, the one reason that decided it, and every piece of evidence as a person reads it.

func FriendStatus

func FriendStatus(e Evidence, now time.Time, loc *time.Location) Verdict

FriendStatus decides a friend's status from evidence, in order: at a limit is down until the reset; a hook record that does not prove her there is down; a bus that cannot deliver to her is down; otherwise up. Times are shown in loc.

type VerdictFacts

type VerdictFacts struct {
	Friend  string `json:"friend"`
	Verdict string `json:"verdict"` // ok, broken, down, untrue
	Shown   string `json:"shown"`   // state/working (e.g. "up/8") or "-"
	Why     string `json:"why"`
}

VerdictFacts carries the health check verdict and explanation.

func DecideVerdict

func DecideVerdict(df DaemonFacts, hf HarnessFacts, bf BusFacts, wf WorkFacts, shown *ShownEntry, window time.Duration) VerdictFacts

DecideVerdict is the pure function deciding a friend verdict from facts; every count and age is of the window (docs/SPEC-FRIEND.md "Check": the verdicts). The facts decide first, in this order:

  1. broken when the session is marked broken, or deliveries in the window are all failures (delivered > 0 and failed == delivered)
  2. down when her nova-hook record does not prove her there
  3. else ok

Then --shown: when it says up or working and the facts verdict is not ok, the verdict stays the facts' and the why leads with "untrue:", the claim and the facts' reason.

func (VerdictFacts) Line

func (vf VerdictFacts) Line() string

Line renders the CHECK VERDICT line.

type Wall

type Wall struct {
	Profile   string   // the row's profile; "" is sandbox.ProfileFriend
	Self      []string // the program that runs the wall verb, and its arguments before the verb: this nova-friend
	Dir       string   // the friend's working directory
	ConfigDir string   // her CLAUDE_CONFIG_DIR; "" none
	Jobs      []string // her job directories outside Dir
	Reads     []string // what her harness reads beyond the system roots and its own directory
	Deny      []string // the coordinator's self, never written inside the wall (sandbox.LaneProfile.Deny)
	// Tmp is the lane's private temp directory, <lane dir>/tmp: Exec makes it for the
	// run and removes it after, and the wall carries it as --tmp (lane_tmpdir.go).
	Tmp string
}

Wall is the wall every child of a lane runs inside: the wall profile its friend row names (sandbox.LaneProfile), over the friend's directories.

func (Wall) Args

func (w Wall) Args() []string

Args is the wall verb and its flags, up to and with the "--" the command follows.

func (Wall) Exec

func (w Wall) Exec(run Exec) Exec

Exec is run with every lane's child inside the wall: a command whose context is a lane's (LaneContext) runs as `<Self> wall <flags> -- <name> <args>`, in the same directory and with the same stdin; any other runs as it was. With no Self the lane's command is refused, never run unwalled.

type Watch

type Watch struct {
	After      string `json:"after"`
	WakeOffset int64  `json:"wake_offset"`
}

Watch is the cursor of the coordinator's watch (docs/SPEC-FRIEND.md, Watch): the last stream entry id it has seen and the wake file's offset it has read to, so the next run misses nothing and takes no flag.

func ReadWatch

func ReadWatch(stateDir string) (w Watch, found bool, err error)

ReadWatch is the cursor the last run saved; found is false when none ever has.

type WindowRefused

type WindowRefused struct{ Remedy string }

WindowRefused is the window step's answer when the accessibility permission is absent (docs/SPEC-FRIEND.md, Reach). Remedy is what a person grants.

func (WindowRefused) Error

func (w WindowRefused) Error() string

type WindowUse

type WindowUse struct {
	Window string    `json:"window"`
	Used   float64   `json:"used"`
	Status string    `json:"status,omitempty"`
	Resets time.Time `json:"resets,omitzero"`
	At     time.Time `json:"at,omitzero"`
}

WindowUse is one subscription window as the harness last reported it: its name, the fraction used (0 when the report named none: under the harness's warning threshold), the harness's status (allowed, allowed_warning, rejected), when it resets (zero: not said) and when it was read.

func ReadRateLimitEvents

func ReadRateLimitEvents(out string, at time.Time) []WindowUse

ReadRateLimitEvents is the windows a headless run's stream-json output reported in its rate_limit_event lines (rateLimitEvent, limit.go: the unifiedWindows of each window, or the older shape's one window), the last word of each window, in the order the windows were first named, each read at at. A rejected event marks rejected the windows it spent (at 1), or every window it names when none is at 1. A line that is not such an event is skipped.

type WorkFacts

type WorkFacts struct {
	Friend       string `json:"friend"`
	Inbox        int    `json:"inbox"`
	Outbox       int    `json:"outbox"`
	NewestOutbox string `json:"newest_outbox"` // name or "-"
	NewestAt     string `json:"newest_at"`     // RFC3339 or "-"
}

WorkFacts carries facts about inbox and outbox directories.

func (WorkFacts) Line

func (wf WorkFacts) Line() string

Line renders the CHECK WORK line.

Directories

Path Synopsis
Package friendtest holds the test doubles of pkg/friend that tests in other packages share.
Package friendtest holds the test doubles of pkg/friend that tests in other packages share.

Jump to

Keyboard shortcuts

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