friend

package
v1.2.9 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 51 Imported by: 0

Documentation

Overview

Package friend is what a friend's machinery runs to be part of the team (docs/SPEC-FRIEND.md; the model is tla/Friend.tla). One daemon per friend parks on the friend's nova-bus stream and pushes each message into the running session as a turn, beats to the sprint server while it does, and keeps the connection with the coordinator symmetric: a ping every window from the coordinator, answered at once by the daemon and as a turn by the session; no ping for a window and the session is told the coordinator is silent. The rules live here, apart from the transport: Machine is the state the daemon owns, stepped by the events and the clock the daemon hands it, so every rule is tested with no socket and no real time.

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 (
	RuleSession = "session"
	RuleApp     = "app"
)

The rules a Liveness is read by, as the record and the status say them (alive=session|app).

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"
	VerdictDeaf   = "deaf"
	VerdictSilent = "silent"
	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 (act: the pong file holds the nonce), and the pong is on the bus (reply).

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 (
	StatusErrorEvery  = time.Minute
	DeferredSaidEvery = time.Minute
)

StatusErrorEvery bounds how often a status file that cannot be written is said in the record: the loop goes on beating and delivering without it. DeferredSaidEvery bounds how often a deferral still in hand is said.

View Source
const (
	MaxBatch   = 32
	BatchBytes = 256 << 10
)

MaxBatch bounds the messages a one-shot lane takes with its card; batch delivery reads every pending message before Envelope applies TextLimit. BatchBytes bounds how much text: the rest waits for the next turn, oldest first. A turn's text travels as one argument to some harnesses (opencode run), under the platform's argument limit.

View Source
const (
	DaemonPongSubject = "daemon-pong"
	PongSubject       = "pong"
	PingPrefix        = "PING "
)

The subjects of the daemon's own messages on the bus.

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

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

View Source
const (
	PushProved   = "proved"
	PushUnproven = "unproven"
)

The push proof's words in the status file.

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

The sources of a Row.

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 (
	PingEvery = time.Second
	DownAfter = 10 * time.Second
	RowsEvery = time.Minute // how often the friend rows are read again
)

The coordinator's side of the connection (docs/SPEC-FRIEND.md, "The coordinator's ping"): the coordinator is the server, it pings every friend row each PingEvery, and a friend is down once DownAfter passes with no pong.

View Source
const (
	PeerUp   = "up"   // a pong to a ping of the last DownAfter
	PeerDown = "down" // none for DownAfter
)

What the coordinator knows of a friend's connection.

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 (
	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 (
	Connected = "connected" // a ping arrived within the window
	Silent    = "silent"    // no ping for a window; the session was told once
)

The connection: what the daemon knows of the coordinator.

View Source
const (
	Quiet      = "quiet"      // nothing asked, or the last ping was answered
	Challenged = "challenged" // a ping was pushed in; no pong yet
	Deaf       = "deaf"       // challenged for a window with no pong
)

The challenge: what the daemon knows of its own session.

View Source
const (
	Awake = "awake" // a write within IdleAfter, or no card held
	Woken = "woken" // idle for IdleAfter holding cards; one wake turn given
	Noted = "noted" // idle IdleAfter more after the wake; the coordinator told once
)

The idle watch: what the daemon knows of a session holding cards (docs/SPEC-FRIEND.md, idle wake).

View Source
const (
	NotificationStateFile   = "notifications.json"
	NotificationReadyText   = "" /* 188-byte string literal not displayed */
	NotificationBatchPrefix = "NOVA-FRIEND NOTIFICATION "
	NotificationWindow      = 30 * time.Second
	NotificationRetryMax    = time.Minute
)

Notifications use the one receiver and the bus's pending entries, never a second consumer or a card scheduler (docs/SPEC-FRIEND.md, notifications; tla/FriendNotifications.tla).

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 (
	SessionQuiet = 10 * time.Minute
	SessionBound = 5 * time.Minute
	// ProveEvery is how long after the last check went in the next goes in while the
	// session is up, whatever else it says on the bus, timed from the ask: the sprint
	// server counts only an answered check, for sprint.FriendProofLive (fifteen
	// minutes), so a session that answers within SessionBound is proved again before
	// the last proof lapses (ProveEvery + SessionBound < FriendProofLive).
	ProveEvery = 8 * time.Minute
	// ReaskAfter is how long an unanswered check waits before it is asked again when the
	// session has not read it: a queueing harness keeps every copy, so a check is asked
	// again early only once the session has read the last (Presence.Read).
	ReaskAfter = time.Hour

	NoSessionAnswer = "no session answer"
	NotYetAnswered  = "no session answer yet" // the daemon started; nothing has answered

	SessionCheckPrefix = "SESSION CHECK "
	PresenceFile       = "presence.json"

	LogBatch = 1000 // log entries read per step; the rest the next
)

Presence is whether the friend's SESSION is alive, never its daemon (docs/SPEC-FRIEND.md, presence). The finding of 2026-10-04: three friends read up with eight cards each while their harness apps were not running, because the daemon answered every ping itself. The proof is the session's: after SessionQuiet with no bus message from the session, the daemon delivers a session check carrying a fresh nonce through the adapter, the way a card goes in; only the session's own reply carrying that nonce counts; SessionBound with none and the friend is down, NoSessionAnswer; the next answer, or any bus message the session writes, brings it back up. The daemon's pong stays the daemon's and never counts.

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

The presence, as the presence file says it.

View Source
const (
	PushRenewEvery  = time.Minute
	PushWriteBudget = 5 * time.Second
)

PushRenewEvery is how often the daemon renews its friend's push proof on the bus while the session stays up: well inside bus.PushFresh, so a live daemon's proof never reads stale, and a dead one's does within PushFresh. PushWriteBudget bounds one write of it.

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"
	PongFile   = "pong.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 pong verb writes Pong. 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 (
	HarnessUnknown = ""
	HarnessRunning = "running"
)

The harness evidence: whether the harness's process was seen in the process table. Advisory, shown and never deciding: a session run from its command line has no app to see, and an app that runs answers nothing. HarnessNotSeen is defined in alive.go ("not-seen").

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 ActedKept = 4096

ActedKept is how many message ids the daemon remembers it pushed into a turn that ended acted: a second delivery of one (the claim hands a message in again when its ack was lost) is dropped and acked, never pushed in twice. Past the memory, the message's receipt says acted all the same (Entry.Stage; docs/SPEC-BUS.md, message-receipts; tla/Bus2Receipts.tla, Take).

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 AliveEvery = 30 * time.Second

AliveEvery is how often the daemon asks the adapter whether its harness is alive (SPEC-FRIEND.md, the harness check): the session's last turn for a headless harness, the app in the process table for one with no headless route; an advisory fact for the status, never presence.

View Source
const AliveWithin = SessionQuiet + SessionBound

AliveWithin is how recent the session's last turn ending exit 0 must be for a headless harness to be alive: a quiet session is sent a session check every SessionQuiet, which has SessionBound to end, so a live session that gets no other turn still ends one within this.

View Source
const AnswerBound = 2 * Window

AnswerBound is how old the session's last answer may be while the friend is up: two windows, a ping each window and a challenge open for less than one, so a session answering every ping is never older than this.

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 BatchTakeEvery = 15 * time.Second

BatchTakeEvery is how often a batch-mode daemon asks the server to take her ready cards.

View Source
const BeatEvery = time.Second

BeatEvery is how often the daemon beats to the sprint server while its loop runs: the sprint's own number (internal/sprint FriendBeatEvery, one second; a friend is down after fifteen without one). It is also the loop's read block: one read of the stream per beat.

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 CodexAppServerBudget = 5 * time.Second

CodexAppServerBudget bounds one connection's dial, handshake and calls.

View Source
const CodexCheckRequeue = ReaskAfter - RecheckEvery

CodexCheckRequeue is how long a request for a pong stands unread in the queue before the same request is queued again in its place: the session check's re-ask (ReaskAfter) less one recheck, so the re-ask at the hour always finds the old one past its age and the session check is never held two hours.

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 DefaultIdleAfter = 10 * time.Minute

DefaultIdleAfter is how long a session holding cards may write nothing before its daemon wakes it, and again before the coordinator is told (the friend row's idle setting, ten minutes when the row says none).

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 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 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 HarnessNotSeen = "not-seen"

What the status says of the harness (Status.HarnessSeen): seen running, or not seen; HarnessUnknown when the adapter cannot tell. A harness run from its command line (dsh headless, codex exec, opencode run, gemini) is read by its session's last turn, never by an app: not seen is never down.

View Source
const HoldersBudget = 10 * time.Second

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

View Source
const HostFile = "host.json"

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

View Source
const IdleWalkEvery = time.Minute

IdleWalkEvery is how often the idle watch reads the session's newest write and the cards she holds.

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 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 LanesFile = "lanes.json"

LanesFile is the lanes' state in the state 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 MaxDeliveries = 3

MaxDeliveries is how many times a message is handed into the session before the daemon gives up on it: a turn that fails leaves the message pending and the bus hands it in again once its claim opens (bus.ClaimAfter); the last failure acks it, with the failure on the record, so a message the session cannot take never comes back for ever. A turn the provider refused (ProviderRefused) counts toward nothing here: the session is at fault, not the message, and BrokenAfter says what happens instead.

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 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 PresentSubject = "present"

PresentSubject is the subject of a friend's own request for the present: a message from her to herself with this subject (nova-bus send --as <me> --to <me> --subject present).

View Source
const PresentTextRule = "nova-friend: PRESENT"

PresentTextRule heads a present's text, so a session and a test know it at a glance.

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 RecheckEvery = 10 * time.Second

RecheckEvery is how long the daemon waits before trying a deferred delivery again (Deferred: the session cannot take a turn now and nothing is wrong). The message stays in the daemon's hand meanwhile: it is never put back on the bus, never counted toward MaxDeliveries, never acked.

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 RestLine = "and %d more: nova-bus recv --as %s --all"

RestLine is the envelope's line for the messages that did not fit in it: how many, and the command that prints every pending message whole.

View Source
const ResumeLabel = "answered by resume, not by the open chat"

ResumeLabel is the line the record carries for a turn answered by resume.

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 SessionCheckFilePrefix = "SESSION-CHECK-"

SessionCheckFilePrefix names the file a claude friend's session check is written as by the folder adapter: <dir>/inbox/SESSION-CHECK-<nonce>, the shape the seat's own folder adapter uses (internal/sprint, PROOF-<nonce>).

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 StaleAfter = 30 * time.Minute

StaleAfter is the stale bound: a delivery after this long with none is a present.

View Source
const StaleTurnLock = time.Minute

StaleTurnLock is how long the turn lock may be held while a headless adapter's own turn record says no turn runs before the session check goes in by the record (docs/SPEC-FRIEND.md, presence: the headless case): one step's race with a turn on its way into the adapter is never read as a stale lock.

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 SupersedeChunk = 256

SupersedeChunk bounds the entries one ack of the superseded carries.

View Source
const SupersededShown = 64

SupersededShown bounds the ids the bus message names; the record says how many in all.

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 WakeMark = "wake=1"

WakeMark marks a ping as a wake check (nova-friend ping --wake): a line of its own in the ping's body.

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).

View Source
const Window = 3 * time.Minute

Window is how long either side waits before it decides the other is gone: the coordinator for a session pong, the daemon for a ping. One number on both sides (the contract of 2026-10-04: "3 minutes to start").

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 AntigravityApp = App{Bundle: "/Applications/Antigravity.app", Name: "Antigravity"}

AntigravityApp is the desktop app the Antigravity adapter delivers into on macOS: the one harness with no headless route, so the one app check.

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 PSArgs = []string{"-axww", "-o", "user=,pid=,args="}

PSArgs is the process listing every check reads: user, pid and the whole command line, never cut.

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-4-5-20251001",
}

ReadModels is the model of each tier for a claude account's reads, the table the bud runners carried.

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 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 AppRunning

func AppRunning(listing, username string, app App) bool

AppRunning says whether listing (ps with PSArgs) has a process of username that is app's main executable: its command line starts inside the bundle and names an executable called app.Name whose path, cleaned, is the bundle's Contents/MacOS/<Name>, so a launch through another spelling of the path (Contents/Resources/../MacOS/<Name>, the finding of 2026-10-06) is the app, and a helper or a longer name is not. ps keeps no argv boundaries, so every place the name ends (a blank, or the line's end) is tried.

func BatchFor

func BatchFor(seat string, msgs []bus.Message, notice, pongCommand string) string

BatchFor is one turn's text: the pong line to run first while a challenge is open, the daemon's word about the coordinator, then every message, oldest first, each as nova-bus recv prints it under a numbered rule, and each labelled by its sender's authority (authored). A single message with nothing else is its authored text alone.

func BatchTakeArgv added in v1.2.7

func BatchTakeArgv(friend string, n int, epoch uint64) []string

BatchTakeArgv is a batch-mode daemon's take of up to n of her ready cards: `take --as friend.<name> --max <n> --epoch <e>`, the server choosing which, in its order, cut to her room. The server refuses every worker's verb sent to it that names no epoch ("a worker's verb sent to the server names the epoch its worker holds"), so the take names the epoch her ready cards were handed at, the newest one her row last said.

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, pong, notice string, seat string, msgs []bus.Message) 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), then what else rides along (the pong line first, the word about the coordinator, the bus messages waiting, each labelled by its sender's authority against seat).

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 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 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 CodexControlSocket

func CodexControlSocket(home string) string

CodexControlSocket is the app-server's control socket under home (CODEX_HOME).

func CodexDequeue

func CodexDequeue(ctx context.Context, s CodexAppServer, thread, id string) (deleted bool, err error)

CodexDequeue withdraws one queued submission from thread's queue; deleted is false when it was no longer there (the session took it).

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>/<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 Envelope

func Envelope(seat string, msgs []bus.Message, now time.Time, me string, limit int, notice, pongCommand string) (text string, shown int)

Envelope is the one turn that carries every pending message when the session is free (docs/SPEC-FRIEND.md, the loop): the pong line first while a challenge is open, the daemon's word about the coordinator, a count, then each message oldest first as `[i/n] <id> from=<f> at=<RFC3339> age=<m>m subject=<s>` and its body, the age taken at now. With limit above zero the text, rest block included, stays within it: a message goes in only while the RestLine for those after it still fits (the first always goes in, even when it alone passes the limit), and the rest are named under RestLine for me by as many of their lines as fit after the count. It answers the text and how many messages it carries, a prefix of msgs: exactly those are acked when the turn is accepted. A single message with nothing else is its authored text alone. Every message retains its sender's authority label (docs/SPEC-FRIEND.md, bus-authority-labels.w3). A function of its arguments.

func ExitTimeout

func ExitTimeout(plist string) time.Duration

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

func FaultDownText

func FaultDownText(friend, 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 FlockHeld

func FlockHeld(path string) bool

FlockHeld says whether another process holds an exclusive flock on path: the probe codex itself makes for a thread's writer lock (try_lock, and WouldBlock means an active writer). A path that does not exist is held by nobody.

func FolderCheckLine

func FolderCheckLine(friend, dir, state string) string

FolderCheckLine is the NOTE install and run print for a claude friend: where her session check lands, and what a live session runs to answer it.

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, 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 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 IsPresentRequest

func IsPresentRequest(friend string, m bus.Message) bool

IsPresentRequest says m is the friend's own request for the present.

func IsRealMessage

func IsRealMessage(subject string) bool

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

func IsWake

func IsWake(text string) bool

IsWake says whether a ping's text asks for a wake check: one of its lines is WakeMark (docs/SPEC-FRIEND.md, session-pong.w1).

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 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 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> subject=... exit=<n>"): when it ended and its exit. No log, or no turn in it, is the zero time.

func LimitAlikeText

func LimitAlikeText(friend, 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, 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, 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) (subject, body string)

LimitUpText is what the seat is told when friend's session answered the wake after the reset.

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 LockPath

func LockPath(home, thread string) string

LockPath is the writer lock codex holds on thread while a process has it open for writing, under home (CODEX_HOME).

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 NewestCodexSession

func NewestCodexSession(home, dir string) (string, error)

NewestCodexSession resolves the saved thread before probing its writer lock (SPEC-FRIEND.md, Codex). Only the index and first session_meta line are read.

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 NotificationCategory

func NotificationCategory(text string) string

NotificationCategory bounds unread useful input to one batch of each category; report backpressure cannot consume the urgent category's capacity.

func NotificationKey

func NotificationKey(text string) string

NotificationKey is the durable enqueue family: one global ready wake, or a batch's immutable message-id fingerprint. Only exact notifier prefixes participate.

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 ParsePing

func ParsePing(text string) (nonce, seat string, since time.Time, ok bool)

ParsePing reads the nonce, seat and since off a ping's text; ok is false when the text is no ping. A ping with no seat line is from an unnamed coordinator: the nonce still counts.

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 PingText

func PingText(seat string, since time.Time, nonce string) string

pingText is the ping as the session reads it: the nonce, the seat, and the one line to run, so a small model gets it right.

func PlanBinary

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

PlanBinary is the path the plist will name. A binary off /Volumes is itself. A binary on /Volumes is copied to InstalledBinary before the plist is written; 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 the session pong as it travels on the bus: pong <nonce> queue=<n> working=<n> width=<n>.

func PongNonce

func PongNonce(body string) (nonce string, ok bool)

PongNonce is the nonce a pong body answers: a daemon-pong's or a session pong's; ok is false when the body is neither.

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 PresentText

func PresentText(friend, seat string, at time.Time, queue []QueueLine, skipped Skipped, note *bus.Message, notice, pongCommand string) string

PresentText is the present turn's text: the pong line first while a challenge is open, the daemon's word about the coordinator, then who she is and who has the seat, her live queue, the skipped line, and the newest coordinator note (as the session reads it under the seat's authority), or that there is none.

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 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 Quoted

func Quoted(m bus.Message) string

Quoted is a message from a sender that is not the seat holder, as the session reads it: a fixed header saying who sent it and that it is data, then every line of the message behind "> ", so no line of it can stand as the daemon's own or as an instruction (docs/SPEC-FRIEND.md, bus-authority-labels.w3).

func RateJudgmentText

func RateJudgmentText(friend string, 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 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 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 ResumeArgs

func ResumeArgs(session, text string) []string

ResumeArgs is the codex command line that resumes session (empty: the newest thread of the working directory) with text as its next turn.

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; its session check goes in by the folder (FolderCheck) for a live session to answer. NewDeliverer answers its batch adapter, the wake file (ClaudeWake); NewClaude is the one that runs cards.

func SessionCheckFile

func SessionCheckFile(dir, nonce string) string

SessionCheckFile is the path a check carrying nonce is written to for the friend whose directory is dir.

func SessionCheckText

func SessionCheckText(nonce, pong, to string) string

SessionCheckText is the 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 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 StaleNonce

func StaleNonce(sent, storeNow time.Time, window time.Duration) bool

StaleNonce says a challenge sent at sent is past the window at storeNow, both the store's clock: dropped, never answered.

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 SupersededNotices

func SupersededNotices(owed []Notice) map[string]string

SupersededNotices is the supersede rule (docs/SPEC-FRIEND.md, the loop): of the daemon's own notices not yet in a turn, oldest first, each of which a newer one exists maps to the newest's id. Those are dropped, never delivered, and each is recorded with superseded=<that id>. It reads only the notices the daemon itself raised, never a message on the bus. A function of its argument.

func SupersededReason

func SupersededReason(at time.Time) string

SupersededReason is the reason a message the present replaced is acked with.

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 Text

func Text(m bus.Message) string

Text is a message as the session reads it, the shape nova-bus recv prints: the header line, a blank line, the body ending in a newline.

func TextLimit

func TextLimit(d Deliverer) int

TextLimit is the most bytes of text d takes as one turn: its own TextLimit() when it has one above zero, else BatchBytes. The daemon's envelope is cut to it (Envelope).

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 UUIDv7Time

func UUIDv7Time(id string) (time.Time, bool)

UUIDv7Time is when a UUIDv7 (Codex's ids) was made: its first 48 bits, Unix milliseconds; ok is false for an id that carries no time.

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 (cmd/nova-sprint, friendFinish, the finish of friend sync, friend reconcile and collect): held is UnaddressedText when report does not address the fix 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 WakePingText

func WakePingText(seat string, since time.Time, nonce string) string

WakePingText is a wake check as the coordinator sends it: the ping, and WakeMark. The daemon answers it at once as any ping and, the session being free, pushes the pong line in as its own turn, so the session is asked even with no message waiting (docs/SPEC-FRIEND.md, session-pong.w1; tla/Friend.tla, WakeTurn).

func WakeTargets

func WakeTargets(me string, rows []WakeRow, never []string) []string

WakeTargets is the friends a wake pass pings (docs/SPEC-FRIEND.md, "The wake ping loop"): the rows whose status is up, never the coordinator itself and never a friend in never, sorted. A friend held or down is not asked: a hold is the coordinator's word and a down friend has no daemon to push the ping in.

func WakeText

func WakeText(nonce string) string

WakeText is the turn that wakes a session after its reset: one word back, the nonce, so only a session that ran this turn answers it.

func WakeTurnText

func WakeTurnText(pongCommand string) string

WakeTurnText is a wake turn's whole text: the exact pong line, and nothing else (docs/SPEC-FRIEND.md, session-pong.w1).

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 WriteLanes

func WriteLanes(stateDir string, s LaneState) error

WriteLanes is the lanes' state written whole to stateDir.

func WriteNotificationState

func WriteNotificationState(dir string, s NotificationState) error

func WritePause

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

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

func WritePong

func WritePong(stateDir string, p Pong) error

WritePong is the pong verb's record of the answer it sent.

func WritePresence

func WritePresence(stateDir string, s PresenceStatus) error

WritePresence writes the presence file, whole.

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
	Width                         int
	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
	// 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 (the wake ping loop, nova-friend ping-install).
	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)
	// NotificationsOnly has its own agent label and carries its policy through install
	// (SPEC-FRIEND.md, notifications); it never replaces the native scheduler.
	NotificationsOnly bool
	NotifyKinds       string
	NotifyWindow      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 isolates notification executables by content hash (SPEC-FRIEND.md, notifications), so installing or rolling one back never overwrites the native binary.

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 Aliver

type Aliver interface {
	Alive(ctx context.Context) Liveness
}

Aliver is an adapter that can say whether its harness is alive, by the cheapest true signal it has: for a harness with a headless program, the session's own last turn (SessionTurns); for one with none, its app in the process table.

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) Alive

func (a *Antigravity) Alive(ctx context.Context) Liveness

Alive: the Antigravity app, whose language server Deliver reaches: no headless route, so the app check, by its bundle and its name.

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.

func (*Antigravity) ReadOnReturn

func (*Antigravity) ReadOnReturn()

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 App

type App struct{ Bundle, Name string }

App is a desktop app as the process table shows it: its bundle and the name of its main executable (Contents/MacOS/<Name>).

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 {
	Run, Check, Pong string
	// StopReturns is how many stop-returns the lanes owe (stop.go, OwedStopReturns): the
	// beat carries it (friend beat --stop-returns) while it is above zero.
	StopReturns int
}

BeatWords are a beat's proof words (nova-sprint friend beat --run, --check, --pong): the daemon's run, the check it put into the session, and the check its session answered, each "" when there is none to say. The sprint server counts an answer only when it names a check this run asked (sprint.ProveBeat).

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 Change

type Change struct {
	Friend   string
	State    string    // PeerUp or PeerDown
	LastPong time.Time // zero when the friend never answered
	Reason   string    // why down: no pong, or the ping could not be sent
}

Change is one friend's connection changing state, the one line the keepalive says: never one per ping.

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)
	ReadPresence func(friend string) (PresenceStatus, bool, error)
	ReadPong     func(friend string) (Pong, bool, error)
	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"`
	Deaf    int `json:"deaf"`
	Silent  int `json:"silent"`
	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) Alive

func (c *ClaudeWake) Alive(context.Context) Liveness

Alive: a claude session is reached only through its own wait on the wake file, and no process says which window runs it.

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 Codex

type Codex struct {
	Dir, Session string
	// QueueOnly keeps notification delivery in the existing app, never a competing exec resume (SPEC-FRIEND.md, notifications).
	QueueOnly bool
	Run       Exec
	Program   string                 // "codex" when empty
	Home      string                 // CODEX_HOME; $CODEX_HOME or ~/.codex when empty
	Held      func(lock string) bool // whether the thread's writer lock is held; FlockHeld when nil
	Env       func(string) string    // getenv; os.Getenv when nil
	Out       io.Writer              // where the turn's output goes, when set: the daemon's record
	// App connects to the Codex app-server, through which the thread's queue is read and
	// withdrawn from (DialCodexAppServer under the home when nil); Now is the clock a queued
	// request's age is read by (time.Now when nil).
	App func(ctx context.Context) (CodexAppServer, error)
	Now func() time.Time
	// contains filtered or unexported fields
}

Codex uses the writer lock to choose its first delivery route. An open chat receives codex queue; a closed chat uses exec resume. A failed route tries the other once, then defers without losing the message (SPEC-FRIEND.md, Codex). Queue acceptance is not a completed answer: the app may start the queued input only after its current turn ends. Without a named session, resolve the newest saved thread for Dir first.

A delivery during a turn is queued, never steered: the open chat's turn runs in the Codex app's own server, which takes no request from outside (CodexAppServer). So the open chat's queue holds at most one request for a pong of each kind (PongRequest): a session check (the presence nonce) and a wake (a wake turn or an idle wake: the coordinator's challenge nonce), each its own series, so one kind never withdraws the other. Before such a delivery is queued the thread's queue is read: a request of the same kind for the same nonce still unread inside CodexCheckRequeue stands for this one, and nothing is queued twice; else the new request is queued first, and only once it is in is every request of its kind it supersedes withdrawn (an older nonce, or the same nonce queued CodexCheckRequeue ago or more), so a queue that fails leaves the old request standing. A queued message that carries anything else (a bus message) is never withdrawn. A message is known by its text's shape: a person who types the exact shape of a pong request into the chat has it treated as one.

func (*Codex) Alive

func (c *Codex) Alive(context.Context) Liveness

Alive: the session's last turn (codex exec resume, codex queue); the ChatGPT app is not read.

func (*Codex) Deliver

func (c *Codex) Deliver(ctx context.Context, text string) (int, error)

func (*Codex) Queued

func (c *Codex) Queued() (n int, ok bool)

Queued is the thread's queue as the last delivery read it; ok is false before one has.

type CodexAppServer

type CodexAppServer interface {
	Call(ctx context.Context, method string, params, result any) error
	Close() error
}

CodexAppServer is one connection to the app-server: one JSON-RPC call at a time.

func DialCodexAppServer

func DialCodexAppServer(ctx context.Context, home string) (CodexAppServer, error)

DialCodexAppServer connects to the app-server under home and initializes the session.

func NewCodexAppServer

func NewCodexAppServer(ctx context.Context, conn net.Conn) (CodexAppServer, error)

NewCodexAppServer speaks the WebSocket handshake and the app-server's initialize over conn.

type CodexQueued

type CodexQueued struct {
	ID, Text string
}

CodexQueued is one message on a thread's queue: its queued submission id and its text.

func CodexQueue

func CodexQueue(ctx context.Context, s CodexAppServer, thread string) ([]CodexQueued, error)

CodexQueue is thread's queue, oldest first (thread/queue/list, every page).

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)
	Pong            func() (Pong, bool, error) // the pong file the pong verb writes: the session ran the line
}

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) Alive

func (d *DSH) Alive(context.Context) Liveness

Alive: the session's last turn (dsh headless --session-id); the DeepSeek Harness app is not read: a headless friend needs no window.

func (*DSH) Deliver

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

func (*DSH) ReadOnReturn

func (*DSH) ReadOnReturn()

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) TurnUnderWay

func (d *DSH) TurnUnderWay() (bool, time.Time)

The headless adapters' own turn records (TurnRecord): each turn a one-shot process into the session.

type Daemon

type Daemon struct {
	Friend, Harness, Dir string
	Width                int
	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
	HarnessStatus        func() (seen, rule string)                        // the beat worker's advisory harness observation; only the loop writes Status
	// Activity is the newest write of the session's files and Cards the ids of
	// the cards she holds, oldest first (nil: the queue file's queued and working
	// tasks under Dir). Activity is read by the independent beat cadence at
	// ActivityEvery; Cards is read by the idle walk at IdleWalkEvery. IdleAfter is her
	// row's idle setting, read each step (nil or zero: DefaultIdleAfter). They
	// drive the idle wake (IdleStep); a nil Activity knows no write, and the
	// watch is off.
	// The same Activity, run at most once an ActivityEvery, is carried on each beat.
	Activity  func() time.Time
	Cards     func() []string
	IdleAfter func() time.Duration
	Now       func() time.Time
	// Pause waits d when the store did not: after a read that answered at
	// once (blocked false: an error, or a store that does not block), and
	// while a delivery runs and the loop only peeks.
	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

	// Pong is the session's recorded answer, read each step while a
	// challenge is open (ReadPong over the state files).
	Pong func() (Pong, bool, 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
	// PongCommand is the exact pong line for this friend and nonce (the
	// binary by path, --as, --dir, --redis), put at the head of a turn while
	// a challenge is open, so a small model has one line to run and nothing
	// to fill in.
	PongCommand func(nonce string) string
	// 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 no ping has named the 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 at Width.
	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 keep the one-shot lanes' state (ReadLanes,
	// WriteLanes over the state files); nil keeps it in memory only.
	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, BatchTakeArgv), 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
	// 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)
	// 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)
	// Mailbox is the adapter under Deliver when her harness's session queues what is
	// delivered (Antigravity: a mailbox): a delivery goes in at once, whatever turn runs, so
	// nothing is ever deferred for a turn under way; each step the daemon hands it the
	// clock, off the loop, and it follows the conversation that reads (Antigravity.Follow),
	// which the status says (session_live). Nil for every other harness.
	Mailbox Mailbox
	// Queued is her harness's own queue of deliveries not yet taken, as the adapter last read
	// it (Codex.Queued), on the status each flush; nil, or not known, says none.
	Queued func() (int, bool)
	// Seat is the coordinator seat holder as the sprint server says it. An
	// error, an empty name or a nil Seat is the seat unknown, and while it is
	// unknown no message is delivered as an instruction (BatchFor).
	Seat func(ctx context.Context) (string, error)
	// Proof is whether the push is proved this run and, while it is not, the nonce
	// its session check carries (SessionCheck.Proof); nil is proved (a harness that
	// runs each card as a process of its own has no session to prove). Until it is
	// proved the daemon delivers nothing into the session: no batch turn, no dealt
	// brief, no wake, no idle wake, no lane, no read; it beats, answers pings and
	// keeps every message pending (docs/SPEC-FRIEND.md, The push proof). The status
	// says push=unproven with the nonce and since when.
	Proof func() (proven bool, nonce string)
	// Sent is the session's proof the sprint server last took on her beat (friend
	// beat --pong answered with it), zero before any; the status carries it.
	Sent func() time.Time
	// Session is the id of the session the daemon delivers into, read each step; a change of
	// it is a new session, owed the present (present.go). Nil reads none: the daemon's start
	// and the stale bound still bring the present.
	Session func() string

	// NotificationOnly uses the notification receiver without any sprint or job hooks (SPEC-FRIEND.md, notifications).
	NotificationOnly     bool
	NotificationStateDir string
	Notifications        *NotificationPolicy
	// contains filtered or unexported fields
}

Daemon is one friend's loop: the recv loop over the friend's stream with the deliver adapter, the beat, and the Machine stepped by what arrives. 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) LiveQueue

func (d *Daemon) LiveQueue() []QueueLine

LiveQueue is her live queue: her row as the server last said it (each card, its column and its inbox/<job>/BRIEF.md), else the queued and working tasks of inbox/QUEUE.json.

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 delivery mode and width); every pending message read off the stream when nothing waits on it (a ping is answered by the daemon at once and acked, never pushed in), else one peek, so a ping arriving during a long turn is still answered at once; each running turn's output watched, and a turn silent past SilentStop stopped; the turns' results (exit 0 acks every message a turn carried); then, in batch mode, one turn with every waiting message when the session is free, else an owed wake check pushed in as its own turn holding only the pong line (startWake), and in one-shot mode, each free lane handed its next card with the waiting messages riding along (lanes.go); an independent beat carrying the session's proof; the session's pong; the status. The daemon's own words about the coordinator collapse to the latest and ride in a turn that carries messages or a card, never 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"`     // ok, stale, none
	Connection string `json:"connection"` // string or "-"
	Challenge  string `json:"challenge"`  // string or "-"
	PongAge    string `json:"pong_age"`   // e.g. "4s" or "-"
	Presence   string `json:"presence"`   // up, asleep, down
	SeenAge    string `json:"seen_age"`   // e.g. "4s" or "-"
	// Proof is the session's proof as the server has it from her daemon: pending
	// while the push is unproven (ProofAge since the daemon started waiting), sent
	// once the server took one on her beat (ProofAge the proof's age), none before.
	Proof    string `json:"proof"`     // pending, sent, none
	ProofAge string `json:"proof_age"` // e.g. "4s" or "-"
}

DaemonFacts carries facts about the launchd agent and daemon.

func (DaemonFacts) Line

func (df DaemonFacts) Line() string

Line renders the CHECK DAEMON line.

type DeafChange

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

DeafChange remembers who was last reported deaf, so the coordinator is told once per change and never once per pass.

func (*DeafChange) Report

func (d *DeafChange) Report(deaf []string) []string

Report takes the friends whose session did not answer this pass and answers the sorted names to tell the coordinator, or none when the set is empty or the same as the one last reported. A set that empties is remembered, so the next deaf friend is a change again.

type DealNotice

type DealNotice struct {
	Friend    string      `json:"friend"`
	Cards     []string    `json:"cards,omitempty"`
	Truncated bool        `json:"truncated,omitempty"`
	Entry     string      `json:"entry,omitempty"`
	Message   bus.Message `json:"message,omitempty"`
}

DealNotice is the newest deal notice owed a friend: the friend it is keyed by, the cards it names, whether that list is a truncated subset, and the bus entry and message it stands for (SPEC-FRIEND.md, notifications). A newer notice replaces it; it is withdrawn when all its cards are taken, and a truncated list is never withdrawn because a card it omits may still be held.

type Deferred

type Deferred struct{ Reason, Remedy string }

Deferred is a Deliverer's answer when the session cannot take a turn now and nothing has failed (for example, neither Codex queue nor resume can accept it). The daemon keeps the message in hand, tries again after RecheckEvery, counts nothing toward MaxDeliveries and acks nothing, so a chat open all day loses no message. Remedy is set when no retry can succeed, because the adapter cannot drive the session at all (dsh: a session under an agent preset): what the friend does instead, which nova-friend install and run refuse with (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 into the friend's running session as a turn and normally blocks until the turn ends. Codex queue instead confirms enqueue acceptance; the app runs it after the active turn ends. Exit 0 acks the bus message (SPEC-FRIEND.md, the deliver command and Codex).

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 existing adapter. Default Codex queue and exec-resume behavior is unchanged.

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 {
	Harness     string    // HarnessRunning, HarnessNotSeen or HarnessUnknown; shown, never deciding
	DaemonUp    bool      // the daemon's status file is fresh; shown, never deciding up
	LastAnswer  time.Time // the session's last pong; zero is never
	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 FolderCheck

type FolderCheck struct {
	Friend, Dir string
}

FolderCheck is the claude harness's session-check adapter (docs/SPEC-FRIEND.md, The push proof; the owner, 2026-10-08: "why not, can we fix the harness to do this?"). Claude Code has no command that puts a turn into a running session from outside, and a claude friend's cards run as processes of their own; what a live session can do is watch a folder. So the daemon's SESSION CHECK goes in as one file, <Dir>/inbox/SESSION-CHECK-<nonce>, holding the check's text (the pong command the session runs: nova-friend pong --as <friend> --nonce <nonce>), and the session that answers it proves the push as any session does: its pong on the bus brings the presence up, the proof on bus2:push follows, and the daemon's next beat carries the pong, so the sprint records the session proof natively. One check stands at a time: the file of an earlier nonce is removed when the next is written, so a session in a long turn finds one check, never a pile. The adapter is not passive (the check does go somewhere a session reads) and never ReadOnReturn (a file written is not a file read), so an unanswered check is asked again with the same nonce on the daemon's cadence. A friend with no live session (per-card lanes only, mode batch) answers nothing: her presence reads down with the check's nonce, her cards' finishes stand for her at the server, and nothing refuses her messages on it. The write holds no turn of the session, so the check goes in in place (InPlace; SessionCheck.ask): the file is on disk before the beat ever says the check.

func (*FolderCheck) Deliver

func (f *FolderCheck) Deliver(_ context.Context, text string) (int, error)

Deliver writes the check text as <Dir>/inbox/SESSION-CHECK-<nonce>, the nonce read off the text's first line (SessionCheckPrefix), removing the file of any earlier check. A text that is no session check is refused.

func (*FolderCheck) InPlace

func (*FolderCheck) InPlace()

InPlace: a file write holds no turn; see InPlace.

type FreshTurn added in v1.2.7

type FreshTurn interface {
	DeliverFresh(ctx context.Context, text string) (int, error)
}

FreshTurn is a deliverer whose check goes in as a fresh turn of its own, a session of its own that no listing is read for and none is grown: the presence of a one-shot friend (docs/SPEC-FRIEND.md, presence: a tiny fresh turn per check). A one-shot friend has no long-lived session by design, so a session that is full or missing is never the reason she is down.

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
	// contains filtered or unexported fields
}

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) Alive

func (g *Gemini) Alive(context.Context) Liveness

Alive: the session's last turn (gemini --resume); before the first, a runner that cannot be found is not running.

func (*Gemini) Deliver

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

func (*Gemini) ReadOnReturn

func (*Gemini) ReadOnReturn()

func (*Gemini) TurnUnderWay

func (g *Gemini) TurnUnderWay() (bool, time.Time)

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) Alive

func (g *Grok) Alive(ctx context.Context) Liveness

Alive: a grok window (the TUI process) open in Dir: a pid of the harness's active_sessions.json with Dir as its cwd, in the process table. A window with no monitor is still running; delivery defers on its own.

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 "-"
	SessionLive    string `json:"session_live"` // the conversation a mailbox harness delivers into, or "-"
	Queued         string `json:"queued"`       // the harness's own queue not yet taken (codex), 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 HarnessWatch

type HarnessWatch struct {
	Alive Aliver
	Every time.Duration
	Now   func() time.Time
	// contains filtered or unexported fields
}

HarnessWatch is the harness check beside the daemon's beat (SPEC-FRIEND.md, the harness check). Every Every (AliveEvery when zero) it asks Alive and keeps the answer on the daemon's status (Status.HarnessSeen), saying each change on the record. It is advisory: it never holds the beat back and never makes the friend down. Presence is the session's (SessionCheck): the finding of 2026-10-05 was a friend run from the dsh command line, answering every session check for three hours, held down because no app was in the process table. An adapter that cannot tell changes nothing. The model is app in tla/FriendPresence.tla, read by no rule (SessionShownUp; the witness appholds is this finding).

func WatchHarness

func WatchHarness(d *Daemon, adapter Deliverer) *HarnessWatch

WatchHarness puts a HarnessWatch over adapter beside d's beat; call it once d is built and before Run. Pass the bare adapter, the one NewDeliverer returned: the gates in front of it (SessionCheck.Gate, Limits.Gate) are Deliverers that answer no Alive. A nil adapter is d's Deliver. Alive is optional by assertion: an adapter that is no Aliver cannot tell.

func (*HarnessWatch) Beat

func (w *HarnessWatch) Beat(ctx context.Context, active time.Time) error

Beat is the daemon's beat, with the harness check beside it: it runs the check when one is due, then beats, whatever the check read.

func (*HarnessWatch) Status

func (w *HarnessWatch) Status() (string, string)

Status returns the last advisory observation without sharing the daemon's mutable Status with the independent beat worker.

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 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 InPlace

type InPlace interface {
	InPlace()
}

InPlace marks a Deliverer whose delivery holds no turn of the session (a file write): SessionCheck.ask runs it in place, never on its Go scheduler, so the check is delivered before ask returns and before any beat names it.

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 Keepalive

type Keepalive struct {
	DownAfter time.Duration // DownAfter unless a test shortens it
	// contains filtered or unexported fields
}

Keepalive is the coordinator's state: per friend, the nonces it sent and when, and the last pong to one of them. It is stepped with the clock the loop reads; the loop owns the transport (cmd/nova-friend serve), so every rule is tested with no socket and no real time.

func NewKeepalive

func NewKeepalive() *Keepalive

NewKeepalive is the coordinator's state with no friend yet.

func (*Keepalive) Failed

func (k *Keepalive) Failed(friend, why string)

Failed records that the ping to friend could not be sent, and why.

func (*Keepalive) Friends

func (k *Keepalive) Friends(now time.Time, names []string)

Friends sets who is pinged to the friend rows read at now: a new row is pinged from now, and one no longer read is forgotten.

func (*Keepalive) Names

func (k *Keepalive) Names() []string

Names is the friends pinged, sorted.

func (*Keepalive) Pong

func (k *Keepalive) Pong(friend, nonce string, now time.Time) bool

Pong records a pong from friend with nonce, read at now (the coordinator's clock, never the store's). Only a nonce sent to that friend in the last DownAfter answers, and only once: a stale, replayed or other friend's nonce changes nothing. It answers whether the pong counted.

func (*Keepalive) Sent

func (k *Keepalive) Sent(friend, nonce string, at time.Time)

Sent records a ping to friend with nonce at at.

func (*Keepalive) Step

func (k *Keepalive) Step(now time.Time) []Change

Step is the clock at now: every friend whose state changes, in name order. Up is a pong within DownAfter; down is DownAfter with none, counted from the last pong, else from when the row was first read.

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 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).

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): a daemon starting up sends what is owed first.
	StopReturns []StopReturn `json:"stop_returns,omitempty"`
}

LaneState is what the lanes keep across restarts, in the state directory (lanes.json): each lane's session, so a lane is the same friend's session for its life, the cards set aside after CardTurns, so a restart does not hand them again, and the cards a lane has begun and not ended, so a restart finishes each (a lane's end, lane_end.go). Epoch is the sprint's epoch the sessions were opened under (epoch.go): her row's past it forgets every session, so a full or stale session never carries across a clear.

func ReadLanes

func ReadLanes(stateDir string) (LaneState, error)

ReadLanes is the lanes' state in stateDir; none is an empty state.

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
	Nonce func() string // six random characters when nil
	Down  func(until time.Time, reason string)
	Up    func(nonce string)
	// 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 wake is answered, 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)
	// 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, then waking until a turn answers the current nonce (tla/FriendLimit.tla is owed). Watch reads every command's output for it; Gate holds deliveries while it is down and wakes the session after the reset. The hooks say the change to the sprint: Down once per limit with its reset (friend down --until), Up when a wake answered.

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 a wake after the reset answers its nonce (Gate). A reset that passes with no answer (the wake never reached a session, say) keeps her down: only the session's answer brings her up, never a process seen or not seen in the process table (HarnessWatch is advisory).

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 a wake after the reset is answered. 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 (the daemon keeps the message in hand, counted toward nothing); the first after the reset is a wake turn (WakeText) whose output must carry its nonce, a fresh one each try, before the message goes in; 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.

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.

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 and, while a wake is open, for its nonce.

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 Liveness

type Liveness struct {
	Known, Running bool
	Why            string
	Rule           string
}

Liveness is an adapter's answer about its harness: Known false when the adapter cannot tell (its friend relies on the session check alone), else Running; Why says what was read, and Rule which rule read it: RuleSession (the session's own last turn), RuleApp (a desktop app in the process table), or empty for another signal (a window, a tmux session, a runner on the path).

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 Machine

type Machine struct {
	Window time.Duration // Window unless a test shortens it

	Connection string    // Connected or Silent
	LastPing   time.Time // when the last ping arrived; the start, before any
	Seat       string    // the coordinator the last ping named
	SeatSince  time.Time // since when, as the ping said
	SilentFrom time.Time // when the coordinator went silent, while Silent

	Challenge string    // Quiet, Challenged or Deaf
	Nonce     string    // the nonce of the current challenge
	Asked     time.Time // when it was pushed in
	LastPong  time.Time // when the session last answered a current nonce
	Pongs     int       // session pongs seen, in all

	Idle      string    // Awake, Woken or Noted
	IdleSince time.Time // when the idle measure starts, if no write is newer: the start, or when a card was first held
	WokenAt   time.Time // when the wake turn was given, while Woken or Noted
	WokenSeen time.Time // the newest write the wake was given on; a newer one answers it
}

Machine is the daemon's state. The daemon steps it with the clock it reads (Tick), the pings that arrive (Ping) and the pongs the session records (Pong); each step answers the pushes the session is owed, in order. The daemon owns the transport around it.

func Start

func Start(now time.Time) *Machine

Start is the daemon's state as it comes up at now: connected, with the start standing in for the last ping (a coordinator that never pings is silent one window after the start), and nothing asked of the session.

func (*Machine) IdleStep

func (m *Machine) IdleStep(now, active time.Time, cards int, after time.Duration) (wake, note bool)

IdleStep is the idle watch at now, with active the session's newest write (zero: none known), cards the cards she holds and after the idle setting (docs/SPEC-FRIEND.md, idle wake): holding no card is awake, and the measure starts again when one is held; a write newer than the one the wake was given on is awake again; awake with no write for after is one wake turn; woken for after with no write is one note to the coordinator; noted says nothing more until a write or no card ends it.

func (*Machine) Ping

func (m *Machine) Ping(now time.Time, seat string, since time.Time, nonce string) []Push

Ping is a ping arriving at now from seat (held since since) with nonce: the connection is back if it was silent (the session is told once), and the session is challenged with this nonce, whatever it was before, so only the newest nonce counts (tla/Friend.tla: Ping). The ping itself goes into the session as the message it arrived in; the daemon delivers that, so the pushes here are only the daemon's own words.

func (*Machine) Pong

func (m *Machine) Pong(now time.Time, nonce string) (current bool)

Pong is the session answering nonce at now: the current nonce ends the challenge; a stale one changes nothing and says so (tla/Friend.tla: Pong).

func (*Machine) Tick

func (m *Machine) Tick(now time.Time) []Push

Tick is the clock at now: a window without a ping makes the coordinator silent, said to the session exactly once per outage; a window challenged with no pong makes the session deaf (tla/Friend.tla: Tick).

type Mailbox

type Mailbox interface {
	Follow(ctx context.Context, now time.Time)
	Live() string
}

Mailbox is a harness whose session queues what is delivered: Follow reads who read the deliveries and moves delivery to the conversation that reads, sending again what the old one left unread; Live is the conversation deliveries go to.

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 Notice

type Notice struct {
	Push
	ID string
	// contains filtered or unexported fields
}

Notice is one of the daemon's own words about the coordinator (a Push of Machine's), with the id the daemon gives it when it is said: the record names a dropped notice and its successor by these ids.

type NotificationBatch

type NotificationBatch struct {
	Text     string        `json:"text"`
	Entries  []string      `json:"entries,omitempty"`
	Messages []bus.Message `json:"messages,omitempty"`
	Ready    bool          `json:"ready,omitempty"`
	Accepted bool          `json:"accepted,omitempty"`
}

NotificationBatch is the immutable input kept before enqueue. Accepted means the queue took it, not that the model processed it; recovery then retries only receipts.

type NotificationPolicy

type NotificationPolicy struct {
	Kinds  []string
	Window time.Duration
}

NotificationPolicy selects model notifications. Requests and blockers always keep their payloads; report is on by default; transport ack and routine status are audited.

type NotificationState

type NotificationState struct {
	Pending *NotificationBatch `json:"pending,omitempty"`
	// Report retains the legacy JSON key for the one deferred report or notice batch.
	Report        *NotificationBatch     `json:"report,omitempty"`
	ReportRetryAt time.Time              `json:"report_retry_at,omitzero"`
	Deals         map[string]*DealNotice `json:"deals,omitempty"`
	Ready         bool                   `json:"ready,omitempty"`
	ReadyDue      time.Time              `json:"ready_due,omitzero"`
	NextReady     time.Time              `json:"next_ready,omitzero"`
	RetryAt       time.Time              `json:"retry_at,omitzero"`
	Failures      int                    `json:"failures,omitempty"`
	Audited       int                    `json:"audited"`
	LastID        string                 `json:"last_id,omitempty"`
}

NotificationState is bounded to one active batch, one deferred nonurgent batch and a ready bit. The bus remains the source of every message; filtered entries retain their audit there.

func ReadNotificationState

func ReadNotificationState(dir string) (s NotificationState, err error)

ReadNotificationState and WriteNotificationState use the existing fsynced atomic state writer (SPEC-FRIEND.md, notifications), under the notification-only directory.

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
	// contains filtered or unexported fields
}

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) Alive

func (o *OpenCode) Alive(context.Context) Liveness

Alive: the session's last turn (opencode run, a batch's or a lane's); before the first, a runner that cannot be found is not running.

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) ReadOnReturn

func (*OpenCode) ReadOnReturn()

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 Pong

type Pong struct {
	Nonce   string    `json:"nonce"`
	At      time.Time `json:"at"`
	To      string    `json:"to"`
	Queue   int       `json:"queue"`
	Working int       `json:"working"`
	Width   int       `json:"width"`
}

Pong is the session's last answer, as the pong verb records it beside sending it: the proof the daemon forwards.

func ReadPong

func ReadPong(stateDir string) (p Pong, found bool, err error)

ReadPong is the session's last recorded answer.

type Presence

type Presence struct {
	Quiet, Bound time.Duration

	Up        bool
	Reason    string    // why it is down; empty while up
	LastHeard time.Time // the session's last bus message, or its last answer, while up
	Nonce     string    // the latest check's nonce, until the session answers it
	Asked     time.Time // when the latest check went in
	Open      bool      // the latest check is unanswered and within the bound
	Owed      bool      // a check is due and has not gone in
	Checks    int
	Answers   int
	Answered  string // the nonce the session last answered
	// Read is the latest check read by the session: its headless turn ended, or the
	// adapter saw the session take it (ReadOnReturn). Until then it is asked again only
	// after ReaskAfter, so a session that queues checks never holds a pile of them.
	Read bool
	// Proven is the push proved this run: the session answered a check or wrote on
	// the bus since the daemon started. Until then the daemon delivers nothing
	// (docs/SPEC-FRIEND.md, The push proof); once proved it stays proved for the run.
	Proven bool
}

Presence is the session's proof of life, stepped by the clock and the bus messages the daemon hands it, so every rule is tested with no socket and no real time. It starts down with a check owed: a daemon that came up proves nothing about the session.

func StartPresence

func StartPresence() *Presence

StartPresence is the presence as the daemon comes up at now: down, not yet answered, a check owed at once.

func (*Presence) Answer

func (p *Presence) Answer(now time.Time, nonce string) (current bool)

Answer is the session's reply carrying nonce, at now: the latest check's nonce makes the friend up, late or not; any other, or one already answered, changes nothing (current false).

func (*Presence) Ask

func (p *Presence) Ask(now time.Time, nonce string)

Ask is the check with nonce going into the session at now: the bound runs from here.

func (*Presence) Heard

func (p *Presence) Heard(now time.Time) (rose bool)

Heard is a bus message the session wrote, at now: the session is alive, so the friend is up (rose: it was down). A check still open stays open, its answer still owed: only an answered check proves her session to the sprint server. The daemon's own messages never reach here (SessionCheck.read).

func (*Presence) Tick

func (p *Presence) Tick(now time.Time)

Tick is the clock at now: an open check past the bound makes the friend down, NoSessionAnswer, unless the session wrote on the bus since it went in, and so does silence for SessionQuiet plus SessionBound with it unanswered; a check is owed ProveEvery after the last check went in while up and SessionQuiet after it while down, and an unanswered one the session has not read is asked again only after ReaskAfter.

type PresenceStatus

type PresenceStatus struct {
	Friend    string    `json:"friend"`
	At        time.Time `json:"at"`
	Presence  string    `json:"presence"` // PresenceUp or PresenceDown
	Reason    string    `json:"reason,omitempty"`
	LastHeard time.Time `json:"last_heard"`
	Nonce     string    `json:"nonce,omitempty"`
	Asked     time.Time `json:"asked"`
	Checks    int       `json:"checks"`
	Answers   int       `json:"answers"`
	Answered  string    `json:"answered,omitempty"` // the nonce the session last answered
}

PresenceStatus is the presence file: what status reads.

func ReadPresence

func ReadPresence(stateDir string) (s PresenceStatus, found bool, err error)

ReadPresence is the presence file; found is false when no daemon wrote one.

type PresentPlan

type PresentPlan struct {
	Superseded []string
	Note       *bus.Entry
	Fresh      []bus.Entry
	Requests   int // the friend's own present requests, acked with the rest and counted as nothing
	Skipped    Skipped
}

PresentPlan is the present's decision over the backlog: the entries acked superseded, the newest coordinator note carried in the turn (nil: none), the pings still inside the challenge window (answered by the daemon as any ping, never superseded), and the counts.

func PlanPresent

func PlanPresent(friend, seat string, backlog []bus.Entry, storeNow time.Time, window time.Duration) PresentPlan

PlanPresent is the present's decision over backlog, every message waiting on her stream, oldest first, at the store's time storeNow, with seat the coordinator (the seat holder, else who she reports to; "" unknown). A PING inside window is fresh, answered by the daemon and kept off the skipped line; a SESSION CHECK is never fresh (a run before this one asked it, and its nonce answers nothing this daemon asked). Of the rest the newest from seat that is no deal is the note the present carries; everything else is superseded.

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 Push

type Push struct {
	Subject string
	Text    string
}

Push is text the daemon owes the session as a turn: the ping (with the pong line to run), or a word about the coordinator.

type PushProver

type PushProver struct {
	Friend, Harness string
	Store           bus.Store // the store itself, never the daemon's (DaemonStore)
	Deliver         Deliverer // the adapter the check goes in by
	Now             func() time.Time
	Record          func(line string) // nil records nothing
	// contains filtered or unexported fields
}

PushProver records the friend's inbox push proof on the bus (bus.PushKey; docs/SPEC-BUS.md, bus-requires-inbox-push-proof) from the daemon's presence, as the SessionCheck saves it: nova-bus names shows it, and send and recv say it as a NOTE beside a message to or from a name without one, never a refusal. The proof is the SESSION CHECK round trip: the check carried into the session by the deliver adapter and the session's pong carrying its nonce (SessionCheck, presence.go). While the presence is up the proof is up and renewed every PushRenewEvery; when the presence is down (a check unanswered within its bound, or a daemon that has not yet been answered) the proof is written down at once, so a sender is told the moment the daemon knows. A passive harness (no deliver command: the check only goes on the stream) pushes nothing into the session and is written down whatever its pongs say.

func (*PushProver) Save

func (p *PushProver) Save(inner func(PresenceStatus) error) func(PresenceStatus) error

Save is inner with the proof written first: what the SessionCheck's Save is set to, so each presence the daemon saves is also the bus's proof. A failed write is recorded and tried again at the next save; it never stops the presence file.

func (*PushProver) Step

func (p *PushProver) Step(s PresenceStatus)

Step writes the proof the presence s says, when it differs from the last one written or the last up one is PushRenewEvery old.

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 QueueLine

type QueueLine struct {
	Card, Col, Brief string
}

QueueLine is one card of her live queue as the present names it.

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 ReadOnReturn

type ReadOnReturn interface{ ReadOnReturn() }

ReadOnReturn is an adapter whose delivery returns exit 0 only once the session has taken the text: a headless turn that ran (dsh, gemini, opencode run), or a message the session marked read (antigravity's read.json). A check it delivered is read, and may be asked again on the check cadence.

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
}

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 SessionCheck

type SessionCheck struct {
	Friend string
	Store  bus.Store // the store itself; the daemon is handed DaemonStore
	// Deliver is the adapter the check goes in by, as Gate wrapped it.
	Deliver Deliverer
	Now     func() time.Time
	Nonce   func() string             // a fresh nonce per check
	Text    func(nonce string) string // the check as the session reads it: the one answer line to run
	Record  func(line string)         // nil records nothing
	Save    func(PresenceStatus) error
	Go      func(func()) // runs a check's delivery; nil is a goroutine
	// Run is this daemon's run, its generation, said with every check and answer
	// on the beat: the server counts an answer only to a check the same run asked.
	Run string
	// Keep is the nonce of a check the daemon's last run put into the session and
	// never saw answered (its presence file's nonce): every check carries it until
	// the push is proved, so the session's late answer to the check already queued
	// in it proves the push, whatever restarts came between. "" starts fresh.
	Keep string
	// Mode is the friend's delivery mode as the daemon reads it (ModeBatch or
	// ModeOneShot; "" while it has not read it: before the first up beat, or with
	// no --mode): her check goes in as a fresh turn of its own (FreshTurn) in
	// one-shot mode, never into a growing session, so a full or missing session
	// is never the reason she is down. "" tries the session and falls back to a
	// fresh turn when the session's deliver is refused full or missing, so a
	// one-shot friend whose beat never comes still reads up. nil is a batch friend.
	Mode func() string
	// contains filtered or unexported fields
}

SessionCheck is the daemon's side of presence: it reads the bus log for the session's messages, steps the Presence, puts the check into the session through the adapter (Gate) or, for a harness with no deliver command, on the friend's own stream, and holds the beat back while the session is down (Beat). The daemon's own sends go through DaemonStore, so a message the daemon wrote never passes for the session's.

func (*SessionCheck) Beat

func (s *SessionCheck) Beat(beat func(ctx context.Context) error) func(ctx context.Context) error

Beat is beat held back while the session is down: the sprint server's friend is up only while her session answers. Each call steps the check first.

func (*SessionCheck) BeatAlways

func (s *SessionCheck) BeatAlways(beat func(ctx context.Context) error) func(ctx context.Context) error

BeatAlways is beat with the check stepped first and never held back: a claude friend's (docs/SPEC-FRIEND.md, The push proof), whose cards run as processes of their own and whose presence at the server is their finishes; her session check goes in by the folder (FolderCheck) and its answer rides the beat (Words), so a live session proves the push and no session refuses nothing.

func (*SessionCheck) BeatOr

func (s *SessionCheck) BeatOr(beat func(ctx context.Context) error, down func(ctx context.Context, until time.Time, reason string) error) func(ctx context.Context) error

BeatOr is beat while the session is up, and while it is down the beat that says so (down: until when the daemon next expects an answer, and why: the push unproven with the check's nonce, or no session answer to it), so the sprint server reads her down with the daemon's reason the second it knows (docs/SPEC-FRIEND.md, presence). A nil down holds the beat back instead. Each call steps the check first.

func (*SessionCheck) DaemonStore

func (s *SessionCheck) DaemonStore() bus.Store

DaemonStore is the store the daemon sends through: each message it adds is remembered as the daemon's.

func (*SessionCheck) Gate

func (s *SessionCheck) Gate(inner Deliverer) Deliverer

Gate is inner with the session's turns holding the turn shared, so a check never goes into a session in the middle of a turn. A passive harness (no deliver command) is answered as it is: its check goes on the stream. A harness that opens a session per lane keeps its lanes.

func (*SessionCheck) Present

func (s *SessionCheck) Present() (bool, string)

Present is whether the session is up, and why not.

func (*SessionCheck) Proof

func (s *SessionCheck) Proof() (proven bool, nonce string)

Proof is whether the push is proved this run (the session answered a check, or wrote on the bus, since the daemon started), and while it is not, the nonce the check carries. The daemon delivers nothing until it is.

func (*SessionCheck) Said

func (s *SessionCheck) Said(w BeatWords)

Said is w carried by a beat the server took: each word said is not said again.

func (*SessionCheck) Step

func (s *SessionCheck) Step(ctx context.Context)

Step is one look: the session's messages since the last, the clock, and the check when it is owed.

func (*SessionCheck) Words

func (s *SessionCheck) Words() BeatWords

Words is what the next beat says: the check asked and the check answered that no beat has carried yet. A beat the server took clears them (Said).

type SessionRefused

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

SessionRefused is a Deliverer's answer when the turn's output says the session cannot take a turn at all, whatever the exit code (dsh: a session under an agent preset, a missing provider key; DSHRefusal): a failed delivery, never a delivered one. Reason is one line naming the session and why, what the status and the friend's row say; Detail what to do; Remedy as in Deferred. The daemon marks the session broken with Reason on the first such turn, keeps every message pending and tries again every RecheckEvery, and a turn that succeeds clears it (docs/SPEC-FRIEND.md, "A turn the session cannot take"). As a Deferred it is the same message kept, so a reader of Deferred (PushProof, the check) still sees its 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 SessionTurns

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

SessionTurns is a headless adapter's record of its own last turn into the session (a delivery, a session check, a lane's turn): when it ended, into which session, and how. A harness with a headless program (dsh headless, gemini --resume, opencode run, codex exec) is alive on its session's word: the last turn ended exit 0 within AliveWithin; no desktop app is read (the finding of 2026-10-06: zhi's deliveries through dsh headless answered in 4 s while the DeepSeek Harness app was closed, and the app check said "harness not running"). A deferred turn (nothing ran) is not a turn. The clock is the daemon's, set by WatchHarness; time.Now until then.

func (*SessionTurns) TurnUnderWay

func (s *SessionTurns) TurnUnderWay() (bool, time.Time)

TurnUnderWay is the record's word: a turn has begun and not ended, since the latest's start.

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 Skipped

type Skipped struct {
	Deals, Pings, Notes int
}

Skipped is what one present superseded, by kind: the deals (card ... dealt), the pings (PING and SESSION CHECK whose nonce is past the challenge window), and every other note.

func (Skipped) Total

func (s Skipped) Total() int

Total is every message the present superseded.

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
	Connection string    `json:"connection"`
	LastPing   time.Time `json:"last_ping"`
	Seat       string    `json:"seat"`
	SeatSince  time.Time `json:"seat_since"`
	Challenge  string    `json:"challenge"`
	Nonce      string    `json:"nonce"`
	LastPong   time.Time `json:"last_pong"` // the session's pong, the only answer that ends a challenge
	Pongs      int       `json:"pongs"`
	// LastDaemonPong is when the daemon last answered a ping itself: transport,
	// never the session (docs/SPEC-FRIEND.md, session-pong.w1).
	LastDaemonPong time.Time `json:"last_daemon_pong"`
	Delivered      int       `json:"delivered"` // messages acked this run
	Beats          int       `json:"beats"`
	LastBeat       time.Time `json:"last_beat"`
	BeatError      string    `json:"beat_error,omitempty"`
	Envelope       int       `json:"envelope"`       // messages the last envelope carried (docs/SPEC-FRIEND.md, the loop)
	EnvelopeBytes  int       `json:"envelope_bytes"` // its text's size: at most the deliverer's text limit, the first message always in
	// Push is the push proof this run (PushProved, or PushUnproven while the
	// session has not answered its check: nothing is delivered), PushNonce the
	// check's nonce and PushSince when the daemon started waiting, while unproven;
	// empty for a harness with no session to prove. ProofSent is the session's
	// proof (the presence file's last_heard) the sprint server last took on her
	// beat, zero before any (docs/SPEC-FRIEND.md, The push proof).
	Push      string    `json:"push,omitempty"`
	PushNonce string    `json:"push_nonce,omitempty"`
	PushSince time.Time `json:"push_since,omitzero"`
	ProofSent time.Time `json:"proof_sent,omitzero"`
	// HarnessSeen is what the harness check last read (HarnessRunning,
	// HarnessNotSeen, or empty: cannot tell). Advisory: it
	// never makes the friend down (alive.go, HarnessWatch).
	HarnessSeen string `json:"harness_seen,omitempty"`
	// HarnessAlive is the rule the harness check read it by: RuleSession (the
	// session's last turn) or RuleApp (the app in the process table); empty
	// for another signal, or before a check.
	HarnessAlive string `json:"alive,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"`
	// SessionLive is the conversation a mailbox harness delivers into (Daemon.Mailbox):
	// the one that reads, as the daemon last followed it; empty for every other harness.
	SessionLive string `json:"session_live,omitempty"`
	// Queued is her harness's own queue of deliveries not yet taken, as the last delivery
	// read it (codex: the open chat's queue), when QueueKnown.
	Queued     int       `json:"queued,omitempty"`
	QueueKnown bool      `json:"queue_known,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"`
}

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) Alive

func (s Stub) Alive(context.Context) Liveness

Alive: a stub delivers nothing and watches nothing.

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) Alive

func (t *Tmux) Alive(ctx context.Context) Liveness

Alive: the hosted tmux session exists (`tmux has-session`); the TUI inside it may still be at any state, which the session check alone answers.

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"; Delivery.tla: the pane's prompt is the session's free state, 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 TurnRecord

type TurnRecord interface {
	TurnUnderWay() (running bool, since time.Time)
}

TurnRecord is a headless adapter's own record of its turns: one that delivers by running a one-shot program into the session (dsh headless, gemini --resume), each turn a process that starts and ends. A turn runs while one has begun and not ended, since the latest's start.

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, bound time.Duration, loc *time.Location) Verdict

FriendStatus decides a friend's status from evidence, in order: at a limit is down until the reset; no session answer within bound is down; a bus that cannot deliver to her is down; otherwise up. The harness's process is shown and decides nothing (the finding of 2026-10-05). Times are shown in loc.

type VerdictFacts

type VerdictFacts struct {
	Friend  string `json:"friend"`
	Verdict string `json:"verdict"` // ok, broken, deaf, silent, 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. deaf when a delivery in the window succeeded and neither a session pong nor a real message came back in the window
  3. silent when no delivery was due in the window and nothing came back
  4. down by presence
  5. 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; when the facts verdict is ok but the friend is asleep or its agent is not loaded, the verdict is untrue.

func (VerdictFacts) Line

func (vf VerdictFacts) Line() string

Line renders the CHECK VERDICT line.

type WakeRow

type WakeRow struct{ Name, Status string }

WakeRow is one row of the friends table as the sprint server's coordinator view gives it: the friend's name and her status (up, down or held).

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)
}

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