Documentation
¶
Overview ¶
artifacts.go is the global deliverables index (docs/CHAT-V3.md, Decision 26): one append-only JSONL file under the v3 home, one row per thing the harness produced FOR the person — a generated image, an export, a finished document. It is a citation, not an archive, in exactly the sense task_index.go established: the smallest row that lets a person find the work again, from any directory, by title and recency.
It is GLOBAL where tasks.jsonl is per-project, because the question it answers — "where is that report from Tuesday" — is a cross-project question. Droppings (logs, stubs) are never recorded here; a row in this file is a claim that a person might want the path back.
media_contract.go is the belt's slice of the provider media client — the interface Config.Media carries (docs/MULTIMODAL.md, the v3 revision).
It is an interface for tools_image.go's reason: tests substitute a scripted generator, and the session package must not depend on which struct internal/provider hands over. provider.MediaClient satisfies it as it stands, so the wiring is one assignment with no adapter.
place.go is the one answer to "where does this conversation keep things".
A v3 session is a FOLDER (docs/CHAT-V3.md, Decision 26): the transcript, the working state, the task checkpoint, the node journals, the droppings, the worktrees and — for an owned session — the workspace itself all live inside one directory, so deleting a session is removing one folder and exporting one is zipping one. Every path below starts as arithmetic on Place.Dir; only Place.Trees resolves that spelling against the disk, because git records worktree paths after resolving symlinks. The other disk access here is Meta's load and save, because where things live and what lives there are one decision made in one file.
The zero Place is the LEGACY layout: every method on it answers "", and a caller holding one keeps deriving sidecar paths the flat way. That is what lets the new layout land seam-first without a flag day.
recentplace.go lists the conversations a directory holds when that directory may hold them in EITHER shape.
A v3 session is a folder (place.go, and docs/CHAT-V3.md's Decision 26), and every session written before that decision is a flat `<stem>.jsonl` beside its siblings. Both are real on the same disk for as long as the migration takes, and a picker that could read only one of them would be a picker that loses a person's work on the day they upgrade — so this reads both and says nothing about which was which.
It is Recent with two differences and no third:
- A FOLDER IS A CANDIDATE WHEN IT HOLDS A TRANSCRIPT, and the path answered for it is that transcript, because a transcript is what a caller opens.
- THE FOLDER'S OWN RECORD OUTRANKS THE JOURNAL. meta.json is written for exactly this reading (place.go's Meta): its title is the name the session settled on, and its last-user stamp is the ordering law — a background write touching a file is not a person returning to a conversation, which is why neither the file's modification time nor the newest line in it is allowed to decide the order.
Recent is left standing rather than taught the second shape, because it is the FLAT layout's reader and dies with it: one function that grew a branch for each layout would be one function two waves have to agree about.
Package session is the v3 conversational agent: a working loop you talk to, not a dispatcher. It owns one conversation against one workspace: the person submits messages, the agent works (read, bash, edit, write, grep, find, ls, todo) and streams what it does as events.
The seams are deliberate and narrow. The agent talks to a provider through Completer (one method), and to the person through a channel of Events. The tasker does not exist here yet: when it attaches, it arrives as extra tools (task/change/stop → store.RequestCommand) registered beside the working ones, and nothing in this file changes.
The loop's wire behavior — message assembly, stop condition, retry schedule, tool parallelism — follows internal/exec/bare, with three deliberate differences: it is interactive (Submit between turns, not one task to the end), interruptible (Interrupt cancels the in-flight turn and keeps the partial), and its compaction follows docs/CHAT-V3.md Decision 9 (omp's architecture: threshold = window − max(15%, 16k), keep 20k tokens verbatim, one LLM summary with omp's section contract, the pass journaled as a transcript marker).
The skills a person hands this conversation by hand.
The shelf is chosen for the model two ways, and they answer different questions. RETRIEVAL asks "which of these look like the work in front of us?", which is a guess and is allowed to be wrong; ATTACHMENT is a person saying "use this one", which is not a guess and may never be scored away. So an attachment is held here by name, rendered ahead of anything retrieval found, and never subject to the catalog's window (skillcatalog.go).
NAMES AND NOT FACTS. Resolution happens where the skills are rendered, against the shelf as it stands at that moment, which is what lets a person attach a skill they are about to install and lets a skill deleted from the shelf simply stop being carried.
The skills one message carries, chosen from the words of the message itself.
The catalog (skillcatalog.go) is the MENU: a windowed, stable section of the prompt prefix that names the shelf. It cannot choose, because its only signal is the workspace path, which does not change between turns. This file is the choosing half: on the text of the message being sent it pins what the words name outright, retrieves what shares words with them, and puts the person's own attachments ahead of both — then renders the result WITH THE TURN rather than in the prefix, because a prefix that changed with every message would be a cache miss on every message (orientation/digest.go, lane/choose.go, prefixbudget_test.go).
THE ORDER IS THE PERSON'S. An attachment is somebody saying "use this one" (skillattach.go), which is not a guess and may never be scored away or windowed: retrieval fills whatever room the attachments leave, and never takes a place from one.
ZERO SKILLS IS ZERO BYTES. A conversation with an empty shelf and nothing attached renders byte-for-byte what it rendered before this file existed, because nothing is appended to the message at all — the same law the catalog section obeys.
sweep.go is the idle reaper (docs/CHAT-V3.md, Decision 26): one pass over the session folders, once per launch, that expires droppings and removes litter.
IT HAS EXACTLY THREE RULES, and the third one outranks the other two.
DROPPINGS EXPIRE. A file under a session's logs/ that nothing has touched for a week is a job log, a stub or a frame nobody is going to read. It is re-creatable by definition — that is what makes logs/ logs/ — so it goes.
LITTER IS REAPED. A session whose recorded workspace or launch directory was under a temp directory is a conversation opened in a place the machine itself considers disposable, and once a week has passed since the person last said anything to it, the whole folder goes. This is the rule that stops the flat layout's failure repeating: nineteen dead /tmp workspace directories on the author's own machine, none of them wanted, none of them removable without reading each one.
NOTHING ELSE IS EVER TOUCHED. transcript.jsonl is FOREVER, work/ is a person's own content, and a session whose transcript is flocked is a live conversation in another window. A sweep that could delete a real transcript would make every one of them provisional, and the whole value of a journal a person can cat, grep and rsync is that it is not.
AND THE AMBIENT SIDE'S OWN LITTER GOES THE SAME WAY. The standing root accumulates two things nobody asked to keep: the folder behind an errand said at home that came to nothing, and the run folder of a firing that delivered nothing. Both are reaped after standing.RunKeep and both are asked the same two questions rule 2 asks — is anybody holding it, and has anything touched it lately — with a third for a run, which is whether the run itself said it came to nothing (standing.RunCameToNothing). The items, the ledgers, the wake log and every run that said, landed or is waiting for the person are outside its reach by construction, and so is everything under v3/projects: rule 4 walks exchanges/ and <id>/runs/ and nothing else, and answers immediately on an empty root.
The two removals are DIFFERENT ACTS and the third rule reads differently against each. Rule 1 reaches into logs/ and nowhere else, so no expiry can ever reach a transcript or a person's work/ whatever its age. Rule 2 removes a whole folder — its work/ and its transcript with it — and is allowed to only because of what it proved first: a temp-rooted session is the disposable kind by construction (Decision 26: "ephemeral" is not a mode a person picks, it is what an owned session in a temp directory already is), and a week has passed since the person last spoke to it. Everything that fails either half of that proof keeps its transcript forever.
The pass is a pure function of a directory and a clock so that it can be proved rather than argued about: SweepPlaces takes both, and every failure is one line to the caller's note and a move to the next folder. A sweep that stopped on the first unreadable directory would be a sweep that never reached the litter.
Index ¶
- Constants
- Variables
- func ActionCategoryWords() string
- func AnswerLabel(kind QuestionKind, key string) string
- func AnswerResolves(answer Answer) bool
- func AnswersPath(sessionDir string) string
- func ArgumentRefusal(text string) bool
- func AskTakeover(sessionDir string) error
- func AudioDir(place Place, workspace string) string
- func BackgroundTold(root string) bool
- func BashBeltAsked() bool
- func BashBoundSeconds(args json.RawMessage, backgroundAfter int) float64
- func BashCommand(text string) (string, bool)
- func BashTimeoutSeconds(args json.RawMessage) float64
- func BeltWorkerBrief(store *plandb.Store, task *plandb.Task, root, resume bool, wake, orders string, ...) string
- func CancelTakeover(sessionDir string)
- func CloseUsage()
- func CompactThreshold(window int) int
- func CompactThresholdFor(model string, window int) int
- func ConsentHead(tool, args string) string
- func ContextFillPinned() (int, bool)
- func ConversationReference(sessionID string, seq int64) string
- func CountUnbilledCall()
- func CountWorking(nodes []WorkNode) int
- func DecisionsPath(sessionDir string) string
- func DecisionsSection(records []DecisionRecord) string
- func EstimateTokens(bytes int) int
- func FlushUsage() bool
- func GenerateImage(ctx context.Context, gen ImageGen, parsed GenerateImageArgs) (string, bool)
- func GroundWord(rung GroundRung, promise TaskMode) string
- func HarnessBelt(workspace string, seams HarnessBeltSeams) []bare.Tool
- func HarnessBeltNames(workspace string, seams HarnessBeltSeams) map[string]bool
- func HasSavedTasks(dir string) bool
- func ImagesDir(place Place, workspace string) string
- func InUse(path string) bool
- func IsNoChangeReply(text string) bool
- func IsUserBashCall(id string) bool
- func LandRunTree(dir, base, title, model string) (branch string, changed []string, refusal string, err error)
- func LastLook(root string) time.Time
- func LastLookAt(root, place string) time.Time
- func LastTold(dir string) time.Time
- func LooseTasksRoot() string
- func MatchQuality(text, needle string) (int, bool)
- func MusicDir(place Place, workspace string) string
- func NewMemoryTidy(parent Config, brainPath, root string, idle standing.Idle) standing.Tidy
- func NewSessionID() string
- func NewStandingRunner(parent Config, root string) standing.Runner
- func NewStandingSentinel(parent Config) standing.Sentinel
- func NewsSubject(conversation string, node uint64) string
- func NoteLook(root string, at time.Time)
- func NoteLookAt(root, place string, at time.Time)
- func NoteTold(dir string, at time.Time)
- func NothingToCompactWhy(err error) (string, bool)
- func OnLaneNews(fn func(LaneNews)) (previous func(LaneNews))
- func OnPhaseNews(fn func(PhaseNews)) (previous func(PhaseNews))
- func OpenRunPlan(dir, title, brief string) (*plandb.Store, error)
- func OpenRunPlanAt(path, title, brief string) (*plandb.Store, error)
- func ParseReasoning(level string) (string, bool)
- func PartialString(argsText, field string) (string, bool)
- func PatchSectionPath(section string) string
- func PatchSections(patch string) []string
- func PeekReport(path string) (string, bool)
- func PlaceSession(place Place) string
- func PlacesRoot() string
- func PlanStorePath(dir string) string
- func PreflightNote(root string, e Elsewhere, parts ...string) string
- func ProgramBadge(name string) string
- func ProgramShellModelRefusal(word string) error
- func ReadRows(transcripts []string) map[string]SessionRow
- func ReasoningKey(model string) string
- func RecordArtifact(path string, artifact Artifact)
- func RecordRoots() []string
- func RecordUnbilledCall(path string, line UsageLine)
- func RecordUsage(path string, line UsageLine)
- func RegisterRunEngine(engine RunEngine)
- func ResolveProgramShellModels(words string, available []string, sources modelsource.Set) (string, error)
- func RunsRoot() string
- func SaveMeta(dir string, meta Meta) error
- func SeatValid(word string) bool
- func ServesModel(sources modelsource.Set, model string) bool
- func SetArchived(dir string, archived bool) error
- func SetOfferAnswerer(answerer offerAnswerer) (previous offerAnswerer)
- func SetProgramAnswerAttribution(folder *ProgramFolder, named bool) error
- func SetRunningCLI(path string)
- func SetTaskArchived(dir, sessionID, taskID string, archived bool) error
- func SetTeamResume(open func(file, workspace string) error)
- func SharedFiles(one, two []string) []string
- func ShortCaption(line string) string
- func SpendShare(usd, budget float64) float64
- func SpendToday(lines []UsageLine, now time.Time) float64
- func SpokeIn(path string) (spoken bool, sure bool)
- func StandingChangeHint(item standing.Item) string
- func StandingChangeWord(item standing.Item) string
- func StandingHead(item standing.Item) string
- func StandingIdle() standing.Idle
- func StandingOnceIsAnAnswer(item standing.Item) bool
- func StandingPlainLabel(label string) string
- func StandingWorld(store *standing.Store, workspace, sessionID string) string
- func SteerDelivered(waiting bool) string
- func StopHolder(sessionDir, transcript string, now time.Time) (int, error)
- func StopUsageWriter(path string) bool
- func SubharnessInput(card SubharnessCard) json.RawMessage
- func SummarySkippedWhy(err error) (string, bool)
- func SweepHome(standingRoot string, note func(string))
- func SweepHomeContext(ctx context.Context, standingRoot string, note func(string))
- func SweepPlaces(root string, now time.Time, note func(string))
- func SweepPlacesContext(ctx context.Context, root string, now time.Time, note func(string))
- func SweepStanding(root string, now time.Time, note func(string))
- func SweepStandingContext(ctx context.Context, root string, now time.Time, note func(string))
- func TakeoverPath(sessionDir string) string
- func TaskAgeWord(d time.Duration) string
- func TaskIndexPath(sessionFile string) string
- func TaskKindWord(kind TaskKind) string
- func TaskMatches(entry TaskIndexEntry, query string) bool
- func TaskProposalHead(notice TaskNotice) string
- func TaskReasonOf(ending TaskEnding, report string) string
- func TaskRecordPath(uri string) string
- func TaskReportAccount(report, result string) string
- func TaskResolveEnum() string
- func TaskResolveVerbs() string
- func TaskSlug(title string) string
- func TaskWordsMatch(text, query string) bool
- func TeamWakeNote(text string) bool
- func TellLane(news LaneNews)
- func TellPhase(news PhaseNews)
- func TrustedWindow(window int) int
- func TrustedWindowFor(_ string, window int) int
- func UnattendedNotice(config Config) string
- func UnbilledCalls() int64
- func UnsavedEditsNote(root string) string
- func UsageDrops() int64
- func UsageLedgerPath() string
- func UsageLineDay(line UsageLine) time.Time
- func UsageSubjectWord(kind string) string
- func UserFacingUpdate(text string) (string, bool)
- func VideoDir(place Place, workspace string) string
- func WebFetch(ctx context.Context, fetcher search.Fetcher, url string) (string, bool)
- func WebSearch(ctx context.Context, provider search.Provider, query string, count int) (string, bool)
- func WebSearchCount(asked int) int
- func WithOwnCacheLineage(ctx context.Context) context.Context
- func WorkPath(id string) (kind, rest string)
- func WriteAnswer(sessionDir string, kind QuestionKind, id uint64, key string) error
- type AbandonReason
- type ActionCategory
- type AdmissionContext
- type AdmissionHandle
- type AdmissionOutcome
- type AdmissionQuote
- type Agent
- func (a *Agent) Abandon(reason AbandonReason) (Usage, bool)
- func (a *Agent) AnchorWorkspace(path string) (string, error)
- func (a *Agent) AnswerLaneOffer(yes bool) bool
- func (a *Agent) AnswerSubharness(id uint64, text string, takingOver bool)
- func (a *Agent) ApprovalDial() bool
- func (a *Agent) ApprovalPosture() string
- func (a *Agent) AskQuestion(q Question) (func(), error)
- func (a *Agent) AskRun(ctx context.Context, rootID, question string, earlier []RunAskExchange) (RunAskAnswer, error)
- func (a *Agent) Attach() (events <-chan Event, running bool, stop func())
- func (a *Agent) AttachReplay() (entries []DisplayEntry, events <-chan Event, stop func())
- func (a *Agent) AttachReplayCursor() ([]DisplayEntry, <-chan Event, func(), ReplayCursor)
- func (a *Agent) AttachSkills(names ...string) []string
- func (a *Agent) AttachedSkills() []string
- func (a *Agent) Autonomy() map[AskKind]Policy
- func (a *Agent) Cancel(id string) (string, error)
- func (a *Agent) CancelWithReason(id, why string) (string, error)
- func (a *Agent) ClearAttachedSkills() int
- func (a *Agent) Close() error
- func (a *Agent) Compact(ctx context.Context) error
- func (a *Agent) CompactWithFocus(ctx context.Context, _ string) error
- func (a *Agent) ContextTokens() int
- func (a *Agent) ContinueRun(ctx context.Context, row uint64) (string, error)
- func (a *Agent) ContinueTask(id uint64, words string) error
- func (a *Agent) ConversationEffort() string
- func (a *Agent) Decisions() []DecisionRecord
- func (a *Agent) DefaultEffort() string
- func (a *Agent) Delegates() DelegateReport
- func (a *Agent) DetachSkill(name string) bool
- func (a *Agent) EarlierHistory() EarlierHistory
- func (a *Agent) Elsewhere() Elsewhere
- func (a *Agent) ElsewhereExcept(others ...string) Elsewhere
- func (a *Agent) Facts() Facts
- func (a *Agent) FollowUp(text string) (<-chan Event, error)
- func (a *Agent) Forget(query string) (string, error)
- func (a *Agent) HandUnverifiedToModel(id uint64) error
- func (a *Agent) HarnessDesigns() <-chan Event
- func (a *Agent) HoldQuestion(kind QuestionKind, token string)
- func (a *Agent) HoldTask(id uint64)
- func (a *Agent) Interrupt()
- func (a *Agent) InterruptFor(door StopDoor)
- func (a *Agent) InterruptNamed(door StopDoor, name string)
- func (a *Agent) Land(folder string) (FolderLanding, error)
- func (a *Agent) LandingFor(folder string) (FolderLanding, bool)
- func (a *Agent) Memories(query string) ([]MemoryLine, error)
- func (a *Agent) Model() string
- func (a *Agent) NameTeam(ctx context.Context, titles []string) (string, error)
- func (a *Agent) NeedsPerson() bool
- func (a *Agent) NewsKey() string
- func (a *Agent) NoteConnected(service, account string)
- func (a *Agent) OpenQuestions() []Question
- func (a *Agent) OrchestrateNodeJournal(runID, nodeID string) (string, bool)
- func (a *Agent) OrchestrateSnapshot(id string) (orchestrate.Snapshot, bool)
- func (a *Agent) Orchestrations() <-chan Event
- func (a *Agent) OtherProjects(now time.Time) []OtherProject
- func (a *Agent) PendingConnect() []string
- func (a *Agent) PendingConsent() []uint64
- func (a *Agent) PendingDecisions() []PendingDecision
- func (a *Agent) PendingSubharnessAsk(id uint64) bool
- func (a *Agent) PendingTasks() []uint64
- func (a *Agent) Places() []PlaceRef
- func (a *Agent) PlanAmend(id, text string) error
- func (a *Agent) PlanCancel(id string) error
- func (a *Agent) PlanNote(id, text string) error
- func (a *Agent) PlanNoteFromChat(id, text string) error
- func (a *Agent) PlanPause(id string) error
- func (a *Agent) PlanPriority(id string, n int) error
- func (a *Agent) PlanResume(id string) error
- func (a *Agent) PlanRunSummary(rootID string) (RunPlanSummary, bool)
- func (a *Agent) PlanSpend(since time.Time) []PlanSpendLine
- func (a *Agent) PlanTaskPage(id string) (PlanTaskPage, bool)
- func (a *Agent) PlanTaskWork(id string) (PlanTaskWork, bool)
- func (a *Agent) PlanTasks() []PlanTaskRow
- func (a *Agent) ProjectPresence() []SessionPresence
- func (a *Agent) PromoteCall(callID string) (string, bool)
- func (a *Agent) ProposeTeams(ctx context.Context, in TeamProposalInput) (TeamProposal, error)
- func (a *Agent) Reasoning() string
- func (a *Agent) ReasoningFor(model string) string
- func (a *Agent) ReasoningLevels() map[string]string
- func (a *Agent) RebuildApprovalGate() error
- func (a *Agent) RedoStronger(ctx context.Context, row uint64) (uint64, string, error)
- func (a *Agent) ReferPlace(path string, arrival PlaceArrival) (PlaceRef, error)
- func (a *Agent) RefreshRunSummary(ctx context.Context, rootID string, lastLook time.Time) (RunPlanSummary, bool)
- func (a *Agent) Remember(text string) (string, error)
- func (a *Agent) RememberScoped(text, scope string) (string, error)
- func (a *Agent) Remembers() bool
- func (a *Agent) RemovePlace(path string) error
- func (a *Agent) ReplaceQuestion(ctx context.Context, answer Answer) (<-chan Event, error)
- func (a *Agent) ResolveConflict(id uint64) error
- func (a *Agent) ResolveConnect(id string, approve bool)
- func (a *Agent) ResolveConnectKey(id string, key string)
- func (a *Agent) ResolveConsent(id uint64, allow bool)
- func (a *Agent) ResolveConsentRemember(id uint64, allow bool, scope ConsentScope)
- func (a *Agent) ResolveHarness(id uint64, run bool, model string)
- func (a *Agent) ResolveOrchestrate(id, answer string) (string, error)
- func (a *Agent) ResolveQuestion(answer Answer) error
- func (a *Agent) ResolveStanding(id uint64, answer StandingAnswer)
- func (a *Agent) ResolveSubharness(id uint64, run bool, input json.RawMessage)
- func (a *Agent) ResolveTask(id uint64, answer TaskAnswer)
- func (a *Agent) ResolveUnverified(id uint64, resolution TaskResolution, why string) error
- func (a *Agent) ResolvedApprovalPosture() string
- func (a *Agent) ResolvedEffort() string
- func (a *Agent) ResumeStoppedTurn(ctx context.Context) (<-chan Event, bool)
- func (a *Agent) RetargetTask(id uint64, model string) (ModelLanding, error)
- func (a *Agent) RetryTask(id uint64) error
- func (a *Agent) Rewind() ([]DisplayEntry, error)
- func (a *Agent) RewindAt(index int) ([]DisplayEntry, error)
- func (a *Agent) RewindPoints() []RewindPoint
- func (a *Agent) RunHarnessRequest(ctx context.Context, name, text, model string) (<-chan Event, error)
- func (a *Agent) RunOrchestrate(ctx context.Context, goal, model string, capDollars float64) (string, error)
- func (a *Agent) SendRunNote(rootID, text string) error
- func (a *Agent) SetAPIKey(key string) error
- func (a *Agent) SetApprovalPolicy(policy *approval.Policy)
- func (a *Agent) SetApprovalPosture(posture string) error
- func (a *Agent) SetAutonomy(kind AskKind, policy Policy) error
- func (a *Agent) SetContextWindow(tokens int)
- func (a *Agent) SetConversationEffort(rung string) bool
- func (a *Agent) SetModel(model string)
- func (a *Agent) SetPlaceMode(path, word string) error
- func (a *Agent) SetReasoning(level string)
- func (a *Agent) SetReasoningFor(model, level string)
- func (a *Agent) SetSources(sources modelsource.Set)
- func (a *Agent) SetSpendRail(usd float64) error
- func (a *Agent) SetTaskEffort(id uint64, rung string) error
- func (a *Agent) SettleWrites()
- func (a *Agent) ShortTitle() string
- func (a *Agent) SkillFacts(status string, limit int) ([]store.Fact, error)
- func (a *Agent) SpellOut(ctx context.Context, draft string) string
- func (a *Agent) StandingApprovalPosture() string
- func (a *Agent) StandingExcept(id string) error
- func (a *Agent) StandingHere() (stand []standing.Item, excepted []standing.Item)
- func (a *Agent) StandingPause(id string) (standing.Status, error)
- func (a *Agent) StandingStandDown(id string) error
- func (a *Agent) StandingTrees() []StandingTree
- func (a *Agent) StartDelegate(ctx context.Context, name, brief string) (uint64, string, string, error)
- func (a *Agent) StartTask(ctx context.Context, brief string, solo bool) (uint64, string, string, error)
- func (a *Agent) StartTaskEffort(ctx context.Context, brief string, solo bool, effort string) (uint64, string, string, error)
- func (a *Agent) StateBlock() string
- func (a *Agent) Steer(words string) (<-chan Event, error)
- func (a *Agent) SteerOrchestrate(id, text string) error
- func (a *Agent) SteerRepeatKnown() bool
- func (a *Agent) SteerTask(id uint64, text string) (SteerReceipt, error)
- func (a *Agent) SteerTaskFrom(id uint64, text string, from SteerSource) (SteerReceipt, error)
- func (a *Agent) StillGoing() bool
- func (a *Agent) StopWork() error
- func (a *Agent) SubharnessIntake(name string) (SubharnessCard, error)
- func (a *Agent) SubharnessList() []SubharnessRow
- func (a *Agent) SubharnessRun(ctx context.Context, name string, input json.RawMessage) (uint64, string, error)
- func (a *Agent) Submit(ctx context.Context, text string) (<-chan Event, error)
- func (a *Agent) SubmitBash(ctx context.Context, text string) (<-chan Event, error)
- func (a *Agent) SubmitImage(ctx context.Context, text string, images []Image) (<-chan Event, error)
- func (a *Agent) SubmitStanding(ctx context.Context, text string) (<-chan Event, error)
- func (a *Agent) TakeBackDecision(id uint64) error
- func (a *Agent) TakeoverAsked() bool
- func (a *Agent) TaskBeat(id uint64) string
- func (a *Agent) TaskContextTokens(id uint64) int
- func (a *Agent) TaskEffort(id uint64) string
- func (a *Agent) TaskIndex() []TaskIndexEntry
- func (a *Agent) TaskJournal(id uint64) string
- func (a *Agent) TaskReport() string
- func (a *Agent) TaskUpdates() <-chan Event
- func (a *Agent) Title() string
- func (a *Agent) TitleChanges() <-chan Event
- func (a *Agent) ToolOnBelt(name string) bool
- func (a *Agent) Transcript() []DisplayEntry
- func (a *Agent) Typing()
- func (a *Agent) UnlandedChanges() []StandingChange
- func (a *Agent) UnqueueFollowUp(ch <-chan Event) bool
- func (a *Agent) Usage() Usage
- func (a *Agent) WaitingOn() string
- func (a *Agent) Wakes() <-chan (<-chan Event)
- func (a *Agent) WatchHarnessDesigns() (<-chan Event, func())
- func (a *Agent) WatchOrchestrations() (<-chan Event, func())
- func (a *Agent) WatchQuestions() (<-chan Event, func())
- func (a *Agent) WatchTask(id uint64) (<-chan Event, error)
- func (a *Agent) WatchTaskRoom(id uint64) (<-chan Event, func(), error)
- func (a *Agent) WatchTaskUpdates() (<-chan Event, func())
- func (a *Agent) WatchTitle() (<-chan Event, func())
- func (a *Agent) WatchWakes() (<-chan (<-chan Event), func())
- func (a *Agent) Why() string
- func (a *Agent) WithdrawQuestion(kind QuestionKind, token, reason string)
- func (a *Agent) WorkingNow() []WorkNode
- type AgentKind
- type Answer
- type AnswerAction
- type AnswerOption
- type AnswerScope
- type ApprovalGate
- type Artifact
- type AskKind
- type Asker
- type AskerKind
- type Blank
- type BlankKind
- type Block
- type BlockKind
- type Blocking
- type Budget
- type CheckRun
- type Completer
- type Confidence
- type Config
- type ConsentScope
- type ConversationHistoryReader
- type DaySpend
- type DecidedBy
- type Decision
- type DecisionClause
- type DecisionRecord
- type DecisionVerb
- type DelegateReport
- type DelegateRow
- type DelegateUnknownError
- type Dial
- type DisplayEntry
- type DocumentAnswer
- type DocumentParser
- type DocumentRead
- type EarlierHistory
- type Elsewhere
- type ElsewhereTask
- type ErrSpendStopped
- type Event
- type EventKind
- type Exchange
- type Expectation
- type FactSource
- type Facts
- type FolderLanding
- type GenerateImageArgs
- type GroundRung
- type HarnessBeltSeams
- type Holder
- type Image
- type ImageGen
- type InputKind
- type InputShape
- type JobKind
- type JobNotice
- type JobState
- type LandedTask
- type Landing
- type LaneNews
- type MediaGenerator
- type MemoryLine
- type Meta
- type ModelLanding
- type ModelSpend
- type NothingToCompact
- type OtherProject
- type OtherProjectTask
- type PendingDecision
- type PendingRunRecord
- type Person
- type Phase
- type PhaseNews
- type Pick
- type Place
- func (p Place) Artifacts() string
- func (p Place) Card() string
- func (p Place) ID() string
- func (p Place) Logs() string
- func (p Place) MetaPath() string
- func (p Place) NodeJournals() string
- func (p Place) State() string
- func (p Place) Tasks() string
- func (p Place) Transcript() string
- func (p Place) Trees() string
- func (p Place) Work() string
- type PlaceArrival
- type PlaceRef
- type PlaceSource
- type PlanAgent
- type PlanCommandPart
- type PlanProgram
- type PlanSpendLine
- type PlanStep
- type PlanTaskNote
- type PlanTaskPage
- type PlanTaskRow
- type PlanTaskWork
- type PlanWorkAgent
- type Policy
- type PolicyKind
- type PresenceJob
- type PresenceQuestion
- type PresenceState
- type PresenceTask
- type Principal
- type ProgramEnding
- type ProgramFolder
- func (f *ProgramFolder) BriefNote() string
- func (f *ProgramFolder) Copied() bool
- func (f *ProgramFolder) Finish(result string) ProgramFolderEnd
- func (f *ProgramFolder) Ground() string
- func (f *ProgramFolder) Hold() *os.File
- func (f *ProgramFolder) IgnoredFile() string
- func (f *ProgramFolder) InputsFile() string
- func (f *ProgramFolder) LeftBehindWords() string
- func (f *ProgramFolder) Plain() bool
- func (f *ProgramFolder) StopPromise() string
- type ProgramFolderEnd
- type ProgramFolderOrder
- type Project
- type ProposedAddition
- type ProposedTeam
- type Question
- type QuestionDiscussion
- type QuestionForm
- type QuestionKind
- type Receipt
- type Record
- type Remains
- type ReplayCursor
- type RequestBooks
- type RequestLine
- type RetryNews
- type RewindPoint
- type RunAdmission
- type RunAskAnswer
- type RunAskExchange
- type RunAskSource
- type RunCharge
- type RunEngine
- type RunLanding
- type RunLimit
- type RunPlanSummary
- type RunSpec
- type RunSummary
- type RunTreeSnapshot
- type Seat
- type SessionLockedError
- type SessionPresence
- func HomePresence(exclude string) []SessionPresence
- func ReadAllPresence(root string, now time.Time, exclude string) []SessionPresence
- func ReadProjectPresence(bucket string, now time.Time, exclude ...string) []SessionPresence
- func ReadSessionPresence(dir string, now time.Time) (SessionPresence, bool)
- type SessionRow
- type SpendDay
- type SpendGuard
- type SpendPrice
- type SpendTask
- type Stakes
- type StaleBuildRow
- type Standing
- type StandingAnswer
- type StandingChange
- type StandingNotice
- type StandingTree
- type SteerMark
- type SteerNote
- type SteerReceipt
- type SteerSource
- type Steward
- type StopDoor
- type SubharnessCard
- type SubharnessField
- type SubharnessMemory
- type SubharnessRow
- type SubharnessRunNote
- type SubjectKind
- type SubjectRef
- type SubjectSpend
- type Summary
- type SummarySkipped
- type TaskAnswer
- type TaskAsk
- type TaskAskKind
- type TaskAskOwner
- type TaskBeatRow
- type TaskCall
- type TaskChangeDisposition
- type TaskCopyRecord
- type TaskCrewRecord
- type TaskEnding
- type TaskFacts
- type TaskGraph
- type TaskIndexEntry
- func LandedTouching(rows []TaskIndexEntry, files []string, after time.Time) (touching, unknown []TaskIndexEntry)
- func LookupTask(rows []TaskIndexEntry, token string) (TaskIndexEntry, bool)
- func ReadTaskIndex(path string) []TaskIndexEntry
- func SearchTaskIndex(rows []TaskIndexEntry, query string, limit int) []TaskIndexEntry
- type TaskKind
- type TaskLanding
- type TaskLanes
- type TaskLiveness
- type TaskMode
- type TaskNode
- type TaskNotice
- type TaskPhaseNotice
- type TaskPresence
- type TaskRecord
- type TaskReplyTag
- type TaskResolution
- type TaskRollup
- type TaskSettle
- type TaskState
- type TaskStatus
- type TaskTier
- type TaskWaitOn
- type TeamLine
- type TeamProposal
- type TeamProposalConversation
- type TeamProposalInput
- type TeamProposalTeam
- type Usage
- type UsageCache
- type UsageGrain
- type UsageLine
- type UsageWindow
- type Withdrawal
- type WorkNode
- type WorkState
- type World
Constants ¶
const ( // HarnessRunKey runs the offered program. HarnessRunKey = "1" // HarnessNotNowKey declines the offer, and costs nothing: the turn the // person typed runs unchanged. HarnessNotNowKey = "2" // HarnessSaveKey keeps the finished design. HarnessSaveKey = "1" // HarnessChangeKey asks for it to be different, and RESOLVES NOTHING — the // page stays exactly where it is, still waiting, and the answer is a // sentence said to the design's own thread ([AnswerResolves] is where that // is enforced, and tui3's harnesscard.go tells the story of what this key // used to do instead: it dropped the page). HarnessChangeKey = "2" // HarnessDropKey throws the page away. HarnessDropKey = "3" )
The keys the harness lane's two questions are answered with.
THE LANE ASKS TWO DIFFERENT QUESTIONS AND THEY DO NOT SHARE A ROW. An OFFER — "run harness research?" — is a permission with a free no; a finished DESIGN is a judgement about a page somebody spent minutes writing, and the three things a person wants to do with it are keep it, ask for it to be different, and throw it away. Both go back through Agent.ResolveHarness and both are QuestionHarness, which is why the keys are spelled here together rather than in two files that would drift.
`1` IS THE YES ON BOTH, which is what lets [Agent.applyToLane] read one digit: `run it` and `save it` are the answer that makes the thing real.
const ( // LandingYesKey accepts the work on the person's word. LandingYesKey = "a" // LandingNoKey says it does not hold. LandingNoKey = "n" // LandingTellKey sends words to the work and LEAVES THE QUESTION OPEN — a // steer never resolves a task by itself (docs/design/task-states/DESIGN.md). LandingTellKey = "s" // LandingAgainKey sends a fresh look at the same working copy. LandingAgainKey = "r" // LandingDecideKey hands this one decision to the model. It is the // one-time `let codeaf decide this one`, and it is never a standing // setting. LandingDecideKey = "d" // LandingTakeBackKey takes back a decision that was settled without the // person. It is on a record rather than on a question, which is why it is // not among the three the row draws. LandingTakeBackKey = "u" )
The keys a landed task's `your call` is answered with, and the two beside them that are not on the row.
THEY ARE LETTERS AND NOT DIGITS, which is the one place this package parts company with AnswerOptions' rule that a key is a digit — because docs/design/task-states/DESIGN.md fixed `a`, `n` and `s` on that card before this file existed, and a hand that learned them there must find them here. Home draws them as chips, so the letters cost nothing there either.
const ( StandingHeadReminder = "wants to remind you" StandingHeadScheduledWork = "wants to schedule work once" StandingHeadCheck = "wants to set up a repeating check" StandingHeadWatch = "wants to watch for something" StandingHeadRule = "wants to keep a rule" )
The heads a standing card opens with. The person's own sentence is the next line, not this one: this line says what KIND of thing is being asked.
const ( // PostureAsk asks about every call the rules say to ask about, and stands // the guardian down even where the settings row has it up: a person who // chose "ask" chose to be asked. PostureAsk = "ask" // PostureGuardian asks, with the small model answering the plainly-safe // calls first (guardian.go). PostureGuardian = "guardian" // PostureAllow is the open gate: the blanket answer becomes allow, exactly // as `--yolo` makes it. The floors hold under it as they hold under the flag. PostureAllow = "allow" // PostureDeny refuses every call the rules do not name. It is a posture a // person chooses on purpose and it is never a stop on the wheel. PostureDeny = "deny" // PostureAuto hands the conversation back to the settings rows as they // stand. It is a stored value and not absence, because absence means "never // touched" and a launch flag still speaks for an untouched conversation. PostureAuto = "auto" )
The postures, spelled once. They are the words `/approvals` takes and the words meta.json keeps; what a PERSON reads for each is the surface's (internal/tui3's approvalchip.go).
const ( CancelTask = "task" CancelRun = "run" CancelHarness = "harness" CancelJob = "job" )
The four kinds of work an id can name, spelled as Agent.Cancel takes them: `task:7`, `run:2`, `harness:4`, `job:3`.
THE PREFIX IS NOT DECORATION. The counters that mint these ids are separate counters — the graph's, the run register's, the harness lane's, the job registry's — so "7" is a task AND a run AND a harness run AND a job, and a surface handing over a bare number would be asking this file to guess which piece of somebody's work to end. A bare number is read as a TASK and only as a task, because that is the id space every surface on this program already had before any of the others existed.
const ( PhaseRunning = provider.PhaseRunning PhaseChecking = provider.PhaseChecking PhaseTidying = provider.PhaseTidying PhaseBriefing = provider.PhaseBriefing PhaseTakingStock = provider.PhaseTakingStock )
The phases a turn has that a request does not. They are spelled in internal/provider for the reason above — one vocabulary — and named here so a reader of this package can see the whole list in one place.
EVERY ONE OF THEM IS HELD OPEN UNTIL IT ENDS, AND IT SAYS ITSELF WHILE IT LASTS. A surface stops drawing a phase it has not heard again for provider.PhaseWindow, and a request's own clock beats inside that window because a stream gives it something to beat on; a turn's phases have no deltas, so this package beats for them ([Agent.tellPhase] and the heart below). It did not, once, and the measured cost of that was a route judge that ran for a quarter of an hour and drew for fifteen seconds of it.
const ( PhaseAsking = provider.PhaseAsking PhaseAllSlow = provider.PhaseAllSlow )
PhaseAsking is a wait a person can end, and PhaseAllSlow is a wait nothing can: every reachable lane is believed slow, acting buys nothing, and saying so IS the act.
THEY ARE THE TRANSPORT'S OWN WORDS AND NOT A SECOND SPELLING OF THEM. The vocabulary is one closed set, owned by the layer that knows what a request is doing (internal/provider's phase.go); these two names exist so that this package and the surface above it can say `session.PhaseAsking` beside every other phase they already name that way, and a build in which the two ever differed would be a surface drawing nothing for a wait the engine was posting.
const ( // ConnectAskHead is the head where the session named the account only by an // id nobody would recognize, which is the one case there is no better word // for. ConnectAskHead = "connect your account?" // ConnectAskReason is why it is being asked now. ConnectAskReason = "the turn asked for something only that account can answer" // ConnectKeyPrompt is the line over the box where the service said nothing // of its own about what it wants. ConnectKeyPrompt = "the key, or the part of the address it is missing" )
The three sentences a connect offer is spelled with. They are constants because the surface builds the same question and the two must not drift.
const ( // TaskPhaseWorking is the node's own worker, in its worktree. TaskPhaseWorking = "working" // TaskPhaseChecking is the gate looking at what the worker left. TaskPhaseChecking = "checking" // TaskPhaseRepairing is a repair round closing named gaps. TaskPhaseRepairing = "repairing" // TaskPhaseSizing is the reading that decides whether this work is handed // out in parts, and how (task_divide.go's [Agent.reviewDivision]). // // IT IS A LIFE OF THE NODE AND NOT A STEP OF A TOOL CALL, which is why it // belongs on this list beside the other three. The reading is a full call to // the tier that thinks — measured at thirteen seconds, and bounded by the // role's own tier — and the worker that asked for it sits inside its own // `divide_work` call for all of it. // // IT IS DRAWN ONLY WHERE SOMEBODY WAITS ON IT (task_divide.go's // [Agent.sizingWait]). It used to be drawn over the drawing the harness put // before a node's first request as well, which held a brand new card on this // word for three and a half minutes on the measured node; that drawing is // weighed beside a worker that is already at work now (task_divide_sketch.go), // and the row says what the worker is doing instead. TaskPhaseSizing = "sizing" )
The lives a running node has, in the plain words every file here writes and a surface draws from.
THEY ARE THE ONE SPELLING. task_beat.go writes these same strings into the node's pulse file for other windows to read, and task_run.go sends them on EventTaskPhase for this one, so a reader outside the process and the card in front of the person are never two vocabularies for the same moment.
const ( // MatchWord is the query standing as a whole word in the text: "auth" in // "fix the auth test". It is the top rung because it is the one a person // means when they type a word and expect the thing they named. MatchWord = 1000 // MatchPrefix is the text STARTING with the query — "pric" over "pricing // research". A thing whose name begins with what you typed is the thing you // were typing the name of. MatchPrefix = 800 // MatchWordStart is some later word starting with it: "res" in "pricing // research". MatchWordStart = 600 // MatchInside is the query somewhere in the text at all. MatchInside = 400 // MatchScattered is the query's letters appearing in order with anything // between them — "prr" over "pricing research". It is the bottom rung // because it is the one that finds things nobody was looking for. MatchScattered = 200 )
The rungs MatchQuality answers with, HIGHEST IS BEST. They are spaced two hundred apart so that a caller may add its own weighting between them — recency, or how much a row wants somebody — without any of it reaching the rung below (internal/tui3's home.go does exactly that).
const ( SubjectTask = "task" SubjectStanding = "standing" SubjectConversation = "conversation" )
The three things money is ever spent ON, in the words a row shows.
THEY ARE A CLOSED THREE BECAUSE THE LEDGER HOLDS THREE IDS. There is no fourth kind hiding in the file: a line was made inside a piece of work, inside a promise the person made, or inside a conversation they were having.
const ( BashEmptyWord = "type a command after !" BashBusyWord = "wait for this turn to finish or stop it before running a ! command" )
These refusals are shared with the composer so a late engine refusal gives the same instruction as the surface's check before spending the draft.
const AdmissionContextVersion = 1
AdmissionContextVersion is the shape of the record below. Another build's checkpoint carries another number and is read through [AdmissionContext.restored] rather than trusted field by field.
const AnswerBanked = "banked"
AnswerBanked is the key a widening answer carries under, in Answer.Comments, when the SURFACE has already written the permission down somewhere the person can find and change it — the shape of a shell command, in the words they picked out of it.
IT IS WHAT KEEPS A NARROW YES FROM WIDENING ITSELF. The session memo this engine writes for a ConsentToolSession answer is keyed by the tool's NAME alone, so on `bash` it means every command for the rest of the conversation — and a person who read `git status*` and pressed a key must not buy silence for `rm -rf`. When the surface has banked a rule the answer is a ConsentRule instead, which is the scope that tells this engine to write nothing beside it (consent.go's askAnswer says the same from the other end).
It is a comment rather than a field for HarnessModelNote's reason: it is one lane's own extra, and every other lane would carry it empty.
const ArtifactsIndexName = "artifacts.jsonl"
ArtifactsIndexName is the file, under the v3 home directory (~/.codeaf/v3/artifacts.jsonl). The caller hands the full path in, for SessionFile's reason: where a person's state lives is the surface's decision.
const BashCeilingSeconds = bare.BashCeilingSeconds
BashCeilingSeconds is THE bound on a foreground bash call — one number, read everywhere, typed once.
── WHY THE CEILING IS THE DEFAULT TOO ──
There used to be two numbers: a 120-second default and a 600-second cap. The gap between them was measured and it was expensive. A scoring script that took three to five minutes hit the 120-second default on every call, was adopted as a job, and answered `still running as job N` — so a model that had asked for nothing of the kind was handed a background job it then had to chase, and the chasing (sleep, tail, sleep, tail) ate 68% of a ten-hour worker's wall clock while a competitor's harness, which simply waited, saw the same score thirty-eight times to our two.
THE MODEL'S FIGURE IS HONOURED UP TO THIS CEILING. A session may hand the still-running command to the job registry sooner through its independently configured background-after clock; setting that clock to zero restores the timeout-only posture. Either handoff preserves the same process.
The number itself lives with the bare tool (bare.BashCeilingSeconds), which now applies it as its own default too — a headless worker nobody is watching used to run `find /` unbounded — so the session, the bare loop and the surface that counts down all read one figure. Exported here so the surface need not know where it is kept.
const BashPromotedLead = "still running as job "
promotedSentence is what a promoted call answers with: the line [Agent.backgroundBash] already speaks for a background start — an id and a path — and then THE OUTPUT THE COMMAND HAS ALREADY PRODUCED.
── WHY THE OUTPUT IS HERE AND NOT LEFT IN THE LOG ──
The id and the path used to be the whole answer, on the reasoning that the model could go and read the rest. What that cost was measured: a model handed a bare id has learned nothing about the work, so its next move is to look at the log — and a command that is still running has usually printed the part that matters (the plan, the first failures, the progress) long before it exits. Handing that back with the id turns a promotion from a question into an answer, and the commonest next call from a model that reads a promotion — go and tail this — stops being worth making.
It is bounded by bare.TailForResult, which is the SAME truncation this command's own result would have been cut by had it finished: last whole lines inside pi's line and byte caps. A promoted call and a finished one are the same command, so the amount of it the model may read is the same number.
THE ID LEADS. Everything downstream reads this sentence from the front — the surface, the tests, a person's eye — and a tail of build output above it would bury the one fact that says what happened.
BashPromotedLead is exported because the surface has to recognize this one result without inventing a second spelling of it. The composer and every reader share the same lead; the job id and the rest of the sentence remain the engine's facts.
const CompactPatience = 2*summaryCallWindow + time.Minute
CompactPatience is how long a surface on the far side of a connection waits for a /compact it asked for. A pass may now ask the model for a summary, and the ten seconds every other call gets is shorter than one summary request on a slow model (twenty seconds each on deepseek-v3.2, 2026-09-28), so the surface said "did not answer in time" about a pass that then landed. Two summary requests is the most a manual pass over one window's worth of conversation makes, and the minute is for the fold and the journal around them.
const ConsentFallbackReason = "it will not run this without your word"
ConsentFallbackReason is why the gate is asking, in the one sentence that is true of every question on this lane whatever the policy matched. The policy's own phrasing is better and rides on the banked question; this is what is left when there is none.
It is exported because a SURFACE builds the same question out of the same request event (tui3's [app.consentQuestion]) and the two are keyed by one token — so a sentence spelled twice would be two questions replacing each other on screen while somebody read one of them.
const ConsentWaiting = "waiting"
ConsentWaiting is the only silence the gate has: an unanswered question stays a question. A surface clock that recorded "denied" after ~10s and cancelled the call was F41; the engine never does that, and every EventConsentRequest says so in Wait.
const DecisionSep = " · "
DecisionSep joins the clauses of a record's line, and it is the separator every telemetry row on every surface uses.
const DefaultRunCapUSD = 100.00
DefaultRunCapUSD is the tank an adaptive run gets when nobody named one, and it is EXPORTED because the composer's own third line opens on this figure (internal/tui3's composerCapDefault). The two were separate literals once, each with a comment telling the other's reader to remember to change it by hand — which is the drift the one-source-of-truth law exists to stop.
It was $2, and real runs hit that gate mid-work often enough that the question became a nag rather than a decision, so the owner raised it to ten; ten did the same thing a year of cheaper models later. A hundred dollars is where a run is genuinely large rather than merely ambitious. The gate has not moved — it is still where more money is asked for — and a caller that names its own cap still wins. Zero from a caller is the run nobody bounded.
const HarnessModelNote = "model"
HarnessModelNote is the key a harness answer carries the model under, in Answer.Comments. It is a comment rather than a field because it is one lane's own extra and every other lane would carry it empty (Event.Model is where the question offered it).
IT IS EXPORTED BECAUSE THE SURFACE FILLS IT. The model the OFFER SHOWED is what the person read before they pressed a key, so it travels back with the answer rather than being looked up again on the far side — where the lane may by then be holding something else (Agent.ResolveHarness takes it).
const (
HarnessPhaseAsking = "awaiting your look"
)
The three phases a design node publishes on TaskNotice.Doing, in the words a person would use about them.
"awaiting your look" is spelled exactly as the surface already spells the one other moment when work is finished and waiting on somebody (internal/tui3's taskUnverifiedWord), because it is the same thing happening: the machine has done its part and the next move is a person's.
HarnessPhaseAsking IS EXPORTED AND THE OTHER IS NOT, and the asymmetry is the point. A surface has to be able to tell this one phase apart from work that is genuinely running, because a design at this phase is NOT running — the machine has finished its part and the only step left is a person's, so it belongs in the tally that says how many things need somebody rather than in the one that says how many things are working (internal/tui3's railGroupOf). Comparing against a string spelled out again over there would be the same fact written down twice, and the second copy would be the one that drifts.
const JobNameWords = 4
JobNameWords is how long a background job's name is allowed to be.
FOUR IS THE LENGTH A PERSON READS AS A LABEL rather than as a sentence, and it is a cap and not a target — a two-word name is left at two. It is exported because the surface that draws the name cuts to the same figure, and a namer asked for more words than the column can show would be paying for words that are thrown away on the way to the screen.
const LandingDecidingWord = "codeaf is deciding"
LandingDecidingWord is that clause, and it is a WHOLE CLAUSE rather than a word: a row reading `nobody could check it · auto` would have told a person the name of a setting instead of who is deciding. It is exported for the one surface that must recognize it (internal/tui3's [app.questionReasonIsNews]): the done card already says who is deciding, so the question repeating the clause alone beside it is a duplication, not news.
const MachineBusy = waitingMachineBusy
MachineBusy is the word a start the governor held is read out with — the rail's `waiting · machine busy`, a plan row's `queued · machine busy` — for a door outside this package that has to say it in the same words.
const MovedWord = TakeoverWord + " · enter on home brings it back"
MovedWord is that sentence with THE WAY BACK on it, and it belongs to the engine road: a conversation the engine holds is opened in another terminal by one keystroke and comes back by the same one, so the window it left names the key rather than reporting a loss (EventMoved).
IT IS BUILT ON TakeoverWord AND NOT WRITTEN A SECOND TIME. A person meets one of these two sentences on the day their conversation walks to another terminal, and two spellings of that would be two programs.
const NoChangeReply = "[no change]"
NoChangeReply is the whole of the model's answer to a carry-on it judges mistaken, and the harness's token for "the answer already given stands".
IT REPLACED "EXPLAIN THE EVIDENCE BRIEFLY, AND FINISH" (#1065). An explanation is words at the end of a turn, and words at the end of a turn are what a surface folds the turn down to — so a model that rightly refused a false "the table was cut off" left the person a rebuttal where the table had been. A token is a thing the model chose to say rather than a phrase the harness thought it heard, for [checkpointNothingLeft]'s reason, and it ends two things at once: carrying on for this ask ([Agent.checkpointReopen]), and its own place as the answer — the transcript keeps it for the model, and every surface leaves it undrawn (IsNoChangeReply) so the settled answer before it stands.
const PartialStringLimit = 8192
PartialStringLimit is how much of a still-arriving string PartialString keeps: the LAST 8k bytes of it. A preview is a tail of a dozen rows, and eight kilobytes is enough of one that no terminal tall enough to show more exists — while keeping what a surface holds per forming call bounded whatever the model is spelling out.
const ProgramNotListeningYet = "has not started reading messages yet"
ProgramNotListeningYet is the starting fact shared by the note refusal and the task page, so both doors describe the same absent inbox in the same words.
const QuestionCap = 3
QuestionCap is how many questions may stand open against ONE piece of work at a time, and it is spelled here and nowhere else.
Past it the asker is refused and told to consolidate: several questions about one task are a sheet, which a person answers in one sitting, and not a queue they meet one at a time over an afternoon. The number is small because the thing it bounds is somebody's attention rather than any resource this program holds — three open decisions about one piece of work is already a piece of work that has stopped.
const ResumedWord = "the reply stopped when this conversation moved — asking again"
ResumedWord is what a person reads when the reply they were waiting for starts again in the window they moved the conversation to.
IT IS [cutShortNotice]'s REGISTER AND FOR ITS REASON. Something took the reply away, something is already being done about it, and there is no decision to make — so the line says what happened and what is happening, in that order, and stops. It is one sentence and not two: the window they came from already said TakeoverWord on its way out.
const ( // RunNotePickupWord is WHEN a note on a run's task is read, and it is ONE // SENTENCE IN TWO PLACES: the receipt a note typed at a run's own row // answers in the chat (stoprun.go's [Agent.sayToRunRow]), and the line the // task room writes under the note once the store has it (internal/tui3's // taskPlanPickupWord takes it from here). A run's task has no worker to // splice a line into; a worker is a separate loop, so the words wait in the // store as a note until the worker asks for its next step. Saying the note // arrived now would claim a read that has not happened. It is exported // because the room must say the same thing about the same note, and the // manual quotes it exactly (worker-harness.md, task-controls.md). RunNotePickupWord = "the worker reads a note at its next step" )
The sentences a receipt can carry, and there is no other.
const SearchDefaultCount = searchDefaultCount
SearchDefaultCount is what a search that asked for nothing gets, exported because the command line's web door defaults the same ask to the same number rather than to a second literal that can drift from this one.
const SpellOutOpening = "taking it to mean —"
SpellOutOpening is the block's first line, and it is EXPORTED because two packages have to spell it the same way: the block is built here and drawn, tested and appended to a draft by internal/tui3. It is one of exactly two phrases this feature adds to the surface's vocabulary — the other is `spell it out`, which is the surface's own — and the em dash is what makes it read as an opening rather than a claim.
const StandingAskLead = "wants to keep an eye on: "
StandingAskLead is the old opening, kept so a reader of an older line can find what a card used to say. New cards open with StandingHead.
const StandingAskReason = "nothing is set up until you say so"
StandingAskReason is why the card is up, in the one sentence that is true of every standing card there is. The when and the cost are the card's to show, in the window where there is room to read them. It is exported for StandingAskLead's reason.
const StandingNoKey = "0"
StandingNoKey is the digit that DECLINES a standing card outright: nothing is created, nothing is run, and the row settles as `not set up` — the answer Agent.ResolveStanding reads out of a zero StandingAnswer.
IT IS A `0` BECAUSE IT MUST NOT BE A DIGIT ANOTHER CHIP ALREADY OWNS, AND MUST NOT BE A LETTER. The card in a conversation numbers its chips by their position — 1 yes, 2 change when, 3 once (tui3's [taskModelKey]) — so a fourth answer taking `4` would move the moment a card drew one chip fewer, and the hand that learned the keys on a watch would decline a reminder. A letter is worse: on home the letters are already typing, which is the whole reason AnswerOption.Key is a digit on every kind. `0` is off the end of the chip numbering in both directions, is one keystroke, and is nowhere near `1`.
AND IT IS A DRAWN CHIP EVERYWHERE, the conversation's own card included. It was a bare key there for a wave — `esc` had always been the no, and the `0` was named only in the hint slot under the message box — and a person meeting their first card said plainly that they could see no way to cancel. A gesture whose only documentation is documentation is the one trade docs/DESIGN-LANGUAGE.md refuses, so the decline is now a chip a person can see and click on all three surfaces, and `esc` goes on doing the same thing beside it in the one place there is an esc to spare.
const StandingOnceKey = "3"
StandingOnceKey is the digit "once, not standing" is answered with, on the card and on home alike. It is spelled once, here, because two surfaces and StandingOptions all have to agree about which chip is the one that may be missing.
const TakeoverStale = 10 * time.Minute
TakeoverStale is how old a request may be and still be answered. A window that asked and was closed removes its request (CancelTakeover); one that was killed cannot, and this is what stops its ask outliving it by a week. It is generous because a holder mid-reply waits for the turn to end before it looks again, and a long reply is minutes, not seconds.
IT IS EXPORTED BECAUSE THE ASKING WINDOW HAS TO KNOW IT. Past this age no holder will ever answer — [takeoverAsked] deletes the request unread — so a surface still waiting on the flock past it is waiting for something that cannot happen, and the one number that says so has to be the same number on both sides of the exchange (ONE SOURCE OF TRUTH).
const TakeoverWord = "moved to another window"
TakeoverWord is the one sentence a surface says about a conversation another window took, and the engine spells it so the event and the surface agree.
const ( // TaskGradeEvidence is how many checked settles of one kind-shaped work it // takes before the store may move a tier. // // It is EXPORTED for one reader: `codeaf models`, which prints a note under // every row that is not yet driving anything and would otherwise print the // router's own gate over a task node's row. Two gates guarding two decisions // is fine; two numbers claiming to be the same gate is the drift the // one-source-of-truth law forbids. // // TWO, and it is deliberately not the ledger's own [router.MinGraded] of // eight. That gate guards a decision this one is not: it decides whether a // learned rating may REORDER A WHOLE PANEL against a cold-start prior, where // being wrong reroutes every leaf of every task (arm B's collapse). This // decides whether ONE PART of one division is done on the careful tier // instead of the cheap one, where being wrong costs the difference between // two models on one piece of work and nothing else — and where the install // that has configured no tiers pays literally nothing, because the ladder // floors on the model the task is already on. // // Two is also the fewest that can tell "keeps getting rejected" from "went // wrong once", which is the sentence the road is built on. TaskGradeEvidence = 2 )
const TaskJournalTail = 512 << 10
TaskJournalTail is what a ROOM asks for: the last of a node's transcript, in bytes.
HALF A MEGABYTE IS THE SCREENFUL WITH ROOM TO SPARE. The page keeps the last [tui3.roomTail] blocks and throws the rest away, and a block is a message — so what is actually drawn is tens of kilobytes even on a node that ran for an hour. The margin is for the one line a journal is allowed to be enormous on: a tool result, capped at 4k by the display but not by the file.
const TaskModelBlank = "model"
TaskModelBlank is the label of the hole a proposal carries when the harness could not settle which model the work runs on, and it is the key the answer carries the chosen one back under (Answer.Blanks).
IT IS ONE NAME READ AT BOTH ENDS. The card fills that map by the blank's own label (tui3's questioninput.go does the filling) and [Agent.applyToLane] reads TaskAnswer.Model straight back out of it, so a label spelled twice would be a choice somebody made and nothing acted on.
const TaskModelPrompt = "run it on {" + TaskModelBlank + "}"
TaskModelPrompt is the sentence the hole sits in, with `{model}` where the hole goes — so what a person reads is `run it on [ anthropic/claude-opus-5 ▾ ]` rather than a form with a field name over it.
const TaskNameWords = orchestrate.NameWords
TaskNameWords is how long a piece of work's name is allowed to be.
THREE IS THE LENGTH A PERSON READS AS A LABEL rather than as a sentence, and it is a cap and not a target — a two-word name is left at two. It is exported because the surface that draws the name cuts to the same figure, and a namer asked for more words than the column can show would be paying for words that are thrown away on the way to the screen (internal/tui3's taskTitleWords).
IT IS ONE FIGURE FOR THE WHOLE PRODUCT and not this package's own. An adaptive run's planner is asked for a name of exactly this length for every node it adds (internal/orchestrate's orchestrate.NameWords, which its law quotes), and those rows stand in the same column beside these ones — so a second number here would be two lengths of name in one list, and the shorter column would be quietly cutting the longer one.
const TaskProposalLead = "wants to start a task: "
TaskProposalLead opens the sentence a task proposal asks with, and it is task.go's own lead repeated here so the card, the presence file and this object cannot become three accounts of one proposal.
IT IS EXPORTED BECAUSE THE SURFACE BUILDS THE SAME QUESTION. A window that draws the proposal has the notice before the questions lane reaches it and raises the question from that, so the two objects must be one sentence — the block keys a question by its lane and its id, and two builders that drifted would put two questions on screen about one proposal.
const TaskProposalPickReason = "it starts on its own unless you say otherwise"
TaskProposalPickReason is why the clock recommends starting it, in the words the recommendation is made in. It is exported for TaskProposalLead's reason.
const TranscriptName = placeTranscript
TranscriptName is what the journal is called inside a session folder. It is place.go's constant under an exported name, for the surfaces that have a path and no Place: a transcript by this name is one a session folder holds, and its directory is therefore the session's (see PlaceSession).
UnauthorizedKeySentence is the ending shared with surfaces that receive only words over the engine wire. Keeping it here lets those surfaces ask for a fresh account read without inventing a second spelling of the refusal.
const UsageLedgerName = "usage.jsonl"
UsageLedgerName is the file, under the v3 home directory (~/.codeaf/v3/usage.jsonl). It is a name beside a path function rather than a literal at every call site, for ArtifactsIndexName's reason: two spellings of one path are two ledgers with half a person's spending in each.
const UserUpdatePrefix = "[update]"
UserUpdatePrefix explicitly addresses an interim response to the person. Ordinary tool narration remains operational work; its wording is never used to guess the intended audience. The marker is retained in the model journal and removed only from the display projection.
Variables ¶
var ( ErrPlainDocument = errors.New("plain text") ErrUnsupportedDocument = errors.New("unsupported document") )
ErrPlainDocument and ErrUnsupportedDocument are the two refusals a caller answers differently from a failure: a command line prints a plain file as-is (no billed call) and refuses the rest in its own words, while the belt points the model at read. Both carry the tool's own sentence as their text, which the belt prints verbatim and the command line never surfaces.
var ApprovalPostures = []string{PostureAsk, PostureGuardian, PostureAllow, PostureDeny, PostureAuto}
ApprovalPostures is every word the door takes, the wheel first.
var ApprovalWheel = []string{PostureAsk, PostureGuardian, PostureAllow}
ApprovalWheel is the wheel's stops in walking order. `deny` and `auto` are deliberately not on it — see the header.
var ErrCompactionInFlight = errors.New("session: a compaction pass is already running")
ErrCompactionInFlight says another pass is already running. The second caller gets an error for the same reason: it did nothing, and it should say so.
var ErrConversationUnchecked error = uncheckedConversation{}
ErrConversationUnchecked is the same refusal for a DIFFERENT reason: the engine cannot say which conversation it has open, so the claim on the send could not be established. It wraps ErrNotThatConversation because a caller has to treat them identically — nothing was delivered, and the words are still theirs. AN UNKNOWN OWNER IS NOT PERMISSION.
var ErrHolderUnknown = errors.New("the window holding this conversation cannot be named")
ErrHolderUnknown is a conversation whose holder cannot be named: no presence record, one too old to trust, or a lock nobody is holding any more.
var ErrLaunchBudget = errors.New("session: launch limit reached")
ErrLaunchBudget identifies a refused turn at the launch's dollar or time limit. Goal ownership and spending authority are independent: choosing a person as principal must not discard a limit supplied at the door.
var ErrNoCrewToRedo = errors.New("no task here to redo · /redo stronger follows a task this conversation started")
ErrNoCrewToRedo is `/redo stronger` in a conversation that has started no routed task.
var ErrNoSessionDir = errors.New("this conversation has no folder to leave a request in")
ErrNoSessionDir is AskTakeover on a conversation with no folder — a memory-only one, or the legacy flat layout — which nothing could ever read.
var ErrNoSkillShelf = errors.New("this conversation has no skill shelf")
ErrNoSkillShelf is the answer Agent.SkillFacts gives a conversation that has no shelf store at all, which is different from a shelf with nothing on it: a surface lists the skill folders it finds either way, and only this answer makes it say on each row that choosing one does nothing.
var ErrNobodyToRead = errors.New("nobody is in there to read your line")
ErrNobodyToRead marks the two refusals that mean the node is STILL RUNNING and simply has no reader inside it right now — mid-check, or landing. It is the fact a surface needs and cannot infer: "there is nobody in there" and "the work is over" are opposite things to offer a person, and only the engine knows which one it just said. Match it with errors.Is; the sentence to show is the refusal's own.
var ErrNotThatConversation = errors.New("that correction was written for another conversation, so it was not sent")
ErrNotThatConversation refuses a send whose conversation is no longer the one open. It is a REFUSAL AND NOT A DELIVERY ELSEWHERE: task 7 in the conversation that replaced it is somebody else's work, and the words stay unsent.
var ErrNothingToCompact = errors.New("session: nothing to compact")
ErrNothingToCompact says a pass found no eligible history to reduce. User instructions and recent work may still fill the window. It is a sentinel rather than a silent no-op so a surface's /compact can say "nothing to compact" instead of reporting a success that changed nothing.
var ErrNothingToRewind = errors.New("session: nothing to rewind")
ErrNothingToRewind says there is no turn to drop: a session that has not been spoken to yet, or one whose entire transcript is a compaction summary. It is a sentinel so the surface can say so in the person's words instead of reporting a success that removed nothing.
var ErrNothingToSteer = errors.New("nothing is running to steer")
ErrNothingToSteer is what Agent.Steer answers when no turn is in flight. Match it with errors.Is; the sentence is the honest one for a surface that has nothing better to say, and a surface with a key to name says so itself.
var ErrSendUnanswered = errors.New("no answer came back, so it is not known whether these words arrived")
ErrSendUnanswered marks a send whose fate the sender DOES NOT KNOW: the words may be on the node's record and may never have left the machine they were typed on, and nothing that can be read from here says which.
IT IS NOT MINTED IN THIS PACKAGE. An engine in this process either takes a line or refuses it, and both of those are answers; the uncertainty belongs to the wire, so internal/remote wraps a call it got no answer to with this and a surface matches it with errors.Is. It lives here because it is the vocabulary a surface reads a send's outcome in, beside ErrNobodyToRead, and because internal/tui3 speaks this package and not that one.
A SURFACE MAY NOT DRAW THIS AS A FAILURE. The one honest reading is "we do not know", and what follows from it is asking again under the same SteerSource where the engine recognises one (Agent.SteerRepeatKnown), and keeping the person's words where they can still see them where it does not.
var ErrSessionLocked = errors.New("session file is open in another codeaf")
ErrSessionLocked is what a second open of a live session file returns. Match it with errors.Is; the *SessionLockedError it wraps carries the path.
var ErrSpendRail = errors.New("session: the spend rail was reached")
ErrSpendRail is what a refused turn carries in its EventError. It is a named sentinel so a surface can match it with errors.Is and say the one thing worth saying — the rail, not a provider fault — instead of matching on words.
var ErrTaskDecided = errors.New("session: that task has already been settled")
ErrTaskDecided says the answer arrived after the question had gone: somebody else settled this node — the model's own `tasks … resolve`, a re-check that finally answered, another window — between the surface drawing the choices and somebody pressing one.
IT IS A SENTINEL BECAUSE THE SURFACE HAS TO TELL IT APART FROM TROUBLE. Every other refusal these doors give means the question is STILL STANDING and the person should try another answer — no working copy, no checker to ask — and a card that answered both by quietly saying "already answered" would be reporting a decision nobody made (internal/tui3's tasksettle.go).
var ErrTaskHandedOver = errors.New("session: that task is already handed to codeaf")
ErrTaskHandedOver says the second press changed nothing because the first one already did it, and it is a SEPARATE sentinel from ErrTaskDecided because the two are opposite facts about the card in front of somebody. A decided node is over and its card stops asking; a handed-over one is still `your call`, still waiting on an answer, and the only thing that moved is whose hands the question is in — so a surface that drew "already answered" over it would be reporting a decision nobody has made (internal/tui3's tasksettle.go).
var ErrTurnInFlight = errors.New("session: a turn is in flight; interrupt it first")
ErrTurnInFlight refuses a rewind while the session is working. Rewinding under a running turn would cut the transcript the turn is mid-way through writing: the loop holds message indices across a provider call (a compaction pass holds one for the whole summary), and a truncation underneath it turns an append-only invariant into a slice out of range.
It is a refusal rather than a wait because a rewind is a person saying "not that" about work they can see. Making them wait for the work they are cancelling to finish first would be the wrong shape; interrupting first and rewinding after is the right one, and it is one keystroke.
var Seats = []Seat{SeatReflex, SeatLow, SeatWorker, SeatHigh, SeatMastermind, SeatJudge, SeatTalk}
Seats is the whole vocabulary, cheapest first, talk last. Talk closes the list rather than opening it because it is not in the tier economy at all: it is the word for the turns a conversation makes for itself, and a settings surface that drew it as a tier would be drawing a dial that answers nothing.
var TaskResolutions = []TaskResolution{TaskAccept, TaskReaudit, TaskRefute}
TaskResolutions are the three answers in the order every place that offers them spells them.
ONE SOURCE OF TRUTH, because this list is written down in three places a model reads and one of them used to be a hand-typed copy: the `tasks` tool's schema enum, that tool's description, and the landing note that tells the model what to type back (task_run.go's [taskNote]). A note offering a verb the schema rejects is the harness teaching a model a word it cannot use, and that is the same defect `propose_task`'s step default had when its schema said 40 and its executor applied 200.
Functions ¶
func ActionCategoryWords ¶
func ActionCategoryWords() string
ActionCategoryWords is the list as the prompt spells it: `search, read, edit, …`. It is built from the table rather than typed into the prompt so a family cannot exist that the model is never told about.
func AnswerLabel ¶
func AnswerLabel(kind QuestionKind, key string) string
AnswerLabel is the word for one key, and "" for a key that kind does not take. A surface says what it just did with it.
func AnswerResolves ¶
AnswerResolves reports whether this answer ENDS the question it was given to, or leaves it open for another one.
IT IS EXPORTED BECAUSE THE SURFACE HAS TO ASK THE SAME QUESTION. A block that took every answer as the end of a question would clear the rows, write the receipt and stop counting the question — while the engine, correctly, left the lane waiting — so the person would be looking at a decision that was still theirs to make with nothing on screen to make it with. One reading, at both ends (tui3's [app.answerQuestion] calls this before it closes anything).
Two answers do it, and both for the same reason: what they send is WORDS, and words are how you ask for something different rather than how you settle.
- `tell it` on a landed task steers the work and leaves the `your call` exactly where it was (docs/design/task-states/DESIGN.md).
- `change it` on a finished design hands the page back to the designer, which rewrites it and puts it in front of you again (HarnessChangeKey).
func AnswersPath ¶
AnswersPath is that doorstep for one session folder.
func ArgumentRefusal ¶
ArgumentRefusal reports whether a tool result's text is a refusal the belt wrote about the call's own arguments: a sentence addressed to the model, one repair away from a working call, about a call that never reached the tool's work.
IT ANSWERS WITH THE LOOP GUARD'S OWN EYES ([argumentRepair], looped.go), so the guard and a surface can never disagree about which failures are the schema talking to the model: a bash that failed out in the world is the person's business and opens itself, while a call refused on its arguments is a repair the model is already making, and the person reads the row's own words rather than the repair instruction.
func AskTakeover ¶
AskTakeover asks the window holding the session in dir to let go of it.
TEMP-AND-RENAME, like presence.json beside it, so the holder's tick never reads half a request. Whether anybody is there to read it is not this function's to say: the caller watches the journal's flock (InUse) and decides by that.
func AudioDir ¶
AudioDir is where a spoken file lands when nobody named a path, MusicDir is where a composed one does, and VideoDir is where a rendered one does. They are ImagesDir with a different legacy leaf and NOTHING else, because the law is about the kind of thing the file is and not about its format: a deliverable is a deliverable whether it is looked at, listened to, or watched (tools_speak.go, tools_music.go, tools_video.go).
func BackgroundTold ¶
BackgroundTold reports whether this store has already been told, once, that background checks are on.
IT IS EXPORTED FOR ONE READER — the surface's /status line, which says WHY nothing is checking (internal/tui3's watchLine). Never told is a machine where nothing stands yet; told, with no timer installed, is a person who turned the row off or an install that did not take, and /status points at the row for both because the row reads `off` in both.
func BashBeltAsked ¶ added in v0.4.0
func BashBeltAsked() bool
BashBeltAsked is [bashBeltAsked] as a door outside this package reads it: a headless errand that dispatches through the run engine needs the same switch the belt and every landing read, and a second reader of CODEAF_TASK_BELT outside this package would be a switch two doors could disagree about. It goes through internal/env, the one door onto the environment, so the compatibility spelling of the variable and the export seam both keep working.
func BashBoundSeconds ¶
func BashBoundSeconds(args json.RawMessage, backgroundAfter int) float64
BashBoundSeconds is the first clock a foreground row will meet. With the background-after clock off it is the command's timeout law unchanged; with it on, the earlier of the two hands the process to the job registry.
func BashCommand ¶
BashCommand recognizes the person's shell prefix, including an empty command so the composer can retain it rather than send it to the model.
func BashTimeoutSeconds ¶
func BashTimeoutSeconds(args json.RawMessage) float64
BashTimeoutSeconds is the bound one foreground bash call actually runs under: the model's own figure when it set a usable one, and BashCeilingSeconds when it did not or when it asked for more.
It is the ONE READ of the timeout argument. The wrapper applies it to the wire args ([withTimeoutLaw]) and the surface counts down against it (internal/tui3's toolLimit), so the number a person watches and the number the command is bounded by cannot drift apart.
AN EXPLICIT NULL IS UNSET, and so is a zero, a negative, a string, a NaN, or anything else that does not read as a positive number of seconds. A weak model that spells every optional argument out — `"timeout": null` — must not be able to disarm the law by saying nothing in more words: that value used to take the "set" branch, decode as 0, pass the cap test untouched, and reach bare as a nil timeout, which arms no timer at all. An unbounded foreground call is a turn that never ends.
func BeltWorkerBrief ¶ added in v0.4.0
func BeltWorkerBrief(store *plandb.Store, task *plandb.Task, root, resume bool, wake, orders string, workspace ...string) string
BeltWorkerBrief composes a run worker's opening document for one store task: the plan-born road ([composeBriefScoped] with the task's id as the scope, so the document OPENS on the task it owns), the work order read FROM the store ([planBrief]) as THE WORK, and the store's deliverables and acceptance as the two sections under it.
A LEAF OWNS A PART OF THE ASK, AND READS THE ASK. A store task's description is the work order — the planning seat's account of one part of the run — and the run's own ask reached the store as the ROOT task's description. A leaf handed only the paraphrase inherits its omissions and cannot notice: on two runs of one objective the planner dropped the same bullet, and only the leaf that read the person's own sentence carried it. So one section of a non-root worker's document carries that sentence VERBATIM — the leaf reads the whole ask even though it owns one part, and where the ask and its work order disagree about a requirement it owns, the ask wins and the leaf says so in its report.
THE ROOT'S OWN DOCUMENT IS UNCHANGED: its work order IS the ask, so there is nothing to put beside it. The section is absent there, and absent when the store carries no root row to read it from — the emptiness law applied to a document, the same reason a request equal to the work is printed once.
THE RESUME CLAUSE IS ADDED, NOT DUPLICATED, the way [childRun.open] adds it to a resumed node's opening: one sentence, appended, when the task's trajectory already has steps — a predecessor was interrupted mid-work, and the effects it left are unannounced facts about the tree this worker is about to act in. THE WAKE CLAUSE, WHEN THERE IS ONE, IS WHAT THE OPENING CARRIES. A worker launched to integrate its children's landings opens on the supervisor's list of what they did — every child's title, status and result — in place of the interrupted-predecessor sentence, which is a fact about a different worker and not about this one. The resume flag still rides the trajectory's steps.
func CancelTakeover ¶
func CancelTakeover(sessionDir string)
CancelTakeover withdraws a request the asking window no longer wants answered — it stopped waiting. A request already taken is nothing to remove, and that is not an error.
func CloseUsage ¶ added in v0.4.0
func CloseUsage()
CloseUsage stops every writer the process registry started and waits, under one ceiling, until each run loop has returned. Closing and detaching happen under the same lock as row enqueue, so no sender can retain a queue after its owner closes it.
func CompactThreshold ¶
CompactThreshold is the law itself — [derivedThreshold] unless a person has pinned a fill, and their fill of the window when they have ([compactThresholdOf] picks between the two) — exported because a surface has to be able to say how close a conversation is to being compacted — and a surface that re-derived the formula from the same two constants would be a second copy of it, free to drift the moment either one moves (internal/tui3 reads this for the status meter's accent).
Zero and negative windows answer zero: a threshold against an unknown window is a number that means nothing, and a caller must have an answer for that rather than treat it as a tiny model.
The window is trusted BEFORE the reserve is taken, so a claim this process has learned to distrust can never move the trigger past what the endpoint was actually willing to serve. That is the guard, and it lives here rather than at the door because the door is not the only way a window arrives.
func CompactThresholdFor ¶
CompactThresholdFor is CompactThreshold for a NAMED model, so that a surface drawing how close a conversation is to being folded reads the same figure the trigger does — including anything this process has learned about what that model's endpoints really serve (TrustedWindowFor).
func ConsentHead ¶
ConsentHead is the permission question's one line for a call: "needs your ok to run bash", and for a manager's `team_start` the sentence the person is actually being asked, "◆ manager wants to start @lexer". args is the call's arguments as JSON, the raw ones or Event.Args; a start whose handle cannot be read falls back to the ordinary line.
IT IS EXPORTED SO THERE IS ONE BUILDER. The card a surface draws and the question home and a second window answer from are the same question, and two builders that drifted would make them two.
func ContextFillPinned ¶
ContextFillPinned is how full a PERSON has said a window may get before it is folded, and whether anybody has said so at all — the settings registry's `context fill` row, the `CODEAF_CONTEXT_FILL_PCT` environment pin behind it, and the `--context-fill` flag that sets that pin for one run (internal/ctxbudget's PinnedFillPercent, which is where those three meet).
It is exported for CompactThreshold's own reason: a surface saying WHY a conversation folds where it folds has to read the rule the trigger reads, rather than open the same three sources for itself and drift from them.
func ConversationReference ¶
ConversationReference is a stable opaque pointer to an indexed message. It carries both keys so a reader copies one value rather than mistaking a global journal sequence for an ordinal inside a conversation. It is a locator, not an authorization token; the inherited reader grants access.
func CountUnbilledCall ¶
func CountUnbilledCall()
CountUnbilledCall lets a provider consumer outside a session agent report the same missing price. The resident leaf path is such a consumer: it has a node banker but no Agent method to receive the reconciliation.
func CountWorking ¶
CountWorking is the one number the head of a rail or a pulse line quotes: every node in the tree that is running right now, at every depth.
func DecisionsPath ¶
DecisionsPath is the record for one session folder.
func DecisionsSection ¶
func DecisionsSection(records []DecisionRecord) string
DecisionsSection is the record as the model's context carries it: a heading and one line per decision, oldest first, bounded to the newest [decisionsSectionMost] with a line saying how many older ones the file still holds.
THE NEWEST ARE THE ONES KEPT, which is the opposite of what the standing section does and is right for the same reason that one leads with the longest-standing: a standing order is a house rule that gets more binding with age, and a decision is an answer to a question that came up — the one given this morning is the one still shaping the work, and the one from eleven questions ago is history. The older ones are named rather than dropped silently, because a model that cannot see them can still go and read them.
It answers with "" for a session that has decided nothing, and a caller adds NOTHING for an empty section — a heading over no lines is the emptiness law broken in the one place it costs tokens as well as clarity.
func EstimateTokens ¶
EstimateTokens is THE estimator this program uses when nobody has told it the real figure: bytes, over [bytesPerToken].
IT IS EXPORTED BECAUSE A SURFACE HAS THE SAME QUESTION AND MUST NOT ANSWER IT SEPARATELY. The engine's books move once a step, when a provider answers with what the step actually cost; a chat drawing a live figure between two of those readings has only the bytes on its own page to go on (internal/tui3's tokencol.go). The day that surface divides by a constant of its own is the day two parts of one program disagree about what a token weighs, and the person reading the screen has no way to tell which of them is lying.
A negative or zero count is nothing rather than a negative estimate: the callers subtract one reading from another, and a subtraction that has not caught up yet is an absence of information, not a debt of tokens.
func FlushUsage ¶
func FlushUsage() bool
FlushUsage waits until every row handed to RecordUsage before this call has reached the disk. It is the seam a process on its way out uses ([v3Process.closeAll] calls it after the conversations have closed, so the last turn's spending is on disk before the terminal comes back), and the one a test uses instead of sleeping — internal/history's Close is the same door for the same reason.
IT IS THE ONE PLACE IN THIS FILE THAT WAITS, deliberately: a caller asking for a flush is asking to be told when the writing is done, and a flush that gave up early would be an answer about nothing. Nothing on a turn path may call it.
IT ANSWERS WHICH OF THE TWO THINGS HAPPENED, true when everything in front of it reached the disk and false when the ceiling fired first. Returning either way is deliberate and stays; what was missing is the one bit the caller cannot work out for itself, and a caller that reads the return as a JOIN when it was a deadline goes on to do something the rows are still in the way of. A test that treats it as a join and then lets its temp directory be removed is exactly that: the writer's next append rebuilds the directory underneath the cleanup. A caller that wants a join wants StopUsageWriter or CloseUsage.
func GenerateImage ¶ added in v0.4.0
GenerateImage is the whole road behind generate_image, as a plain function the command line's image door runs too: pick the model, read the references, send the request, decode, account, write, describe. It exists so the belt's tool and the command line's door cannot drift.
Every failure is the tool error the belt returns and the command line prints, never a Go error: a refused prompt, an expired key, a model having a bad minute are all things a caller can act on, and none of them is a reason to crash anything.
func GroundWord ¶
func GroundWord(rung GroundRung, promise TaskMode) string
GroundWord is the plain words for where one task's work happened: its rung's line in [groundWords], or "" when nothing here can honestly say.
THE PROMISE IS THE FALLBACK AND NOT A SECOND TABLE. A record row written before the ladder existed carries no rung and still carries how the task stood on its ground, and before the ladder a repository got a worktree and a folder got a copy — so the promise answers for exactly the rows the rung cannot, in the words the rung would have used.
A REFERENCE GETS NOTHING, DELIBERATELY. That promise hands the node an EMPTY folder of its own and leaves the ground read-only ([prepareTaskTreeOn]), so both lines of the table would be false about it: it is nobody's copy and it is not the person's folder. A surface handed "" says where the work was without claiming what made it, which is the emptiness law doing its job.
func HarnessBelt ¶
func HarnessBelt(workspace string, seams HarnessBeltSeams) []bare.Tool
HarnessBelt is every tool a saved harness may name on its whitelist, may resolve at run time, and may be told about while it is being designed.
The workspace is the one the tools act in. It is the DESIGNER's workspace when this is called for the guide and the RUN's when it is called for the bridges, and those are the same directory in every door that exists today — but the argument stays explicit rather than being read off a config, because the day they differ the tools must act in the run's and not in the one a page was written in.
func HarnessBeltNames ¶
func HarnessBeltNames(workspace string, seams HarnessBeltSeams) map[string]bool
HarnessBeltNames is the same belt as a set, for the lint that checks a whitelist and for anything else that only needs to ask "may this name be written down here".
func HasSavedTasks ¶
HasSavedTasks protects task-only conversations from empty-launch cleanup. Even unreadable or partly written work must survive: a failed read is not evidence that a folder is disposable.
func ImagesDir ¶
ImagesDir is where a picture lands when nobody named a path: the workspace itself for an owned session, the session's artifacts/ for a borrowed one, and the legacy dot directory for a session with no folder at all.
It is EXPORTED because the engine writes pictures too (internal/remote's image.go) and a picture that landed in two different places depending on which machine received it would be a picture the journal's reference cannot describe in one sentence.
func InUse ¶
InUse reports whether another codeaf is holding this transcript open.
It is the same flock [lockSessionFile] takes, asked as a question rather than as a claim: the lock is tried and released at once, so the answer is "somebody else has it right now" and nothing is left behind. A migration and a launch groom both need it — moving or removing a session another window is writing is the one way either of them could cost somebody a live conversation — and both would rather skip a folder than take one.
A file that is not there, cannot be opened, or sits on a filesystem with no locking answers FALSE, for the reason [lockSessionFile] opens unlocked on such a filesystem: the guard is worth having where it works and is never worth refusing the work over.
func IsNoChangeReply ¶
IsNoChangeReply says whether a response's words are NoChangeReply and nothing else. It is the ONE reading of the token, used by the harness and by every surface that draws an answer, so the two can never disagree about which reply was withdrawn.
func IsUserBashCall ¶
IsUserBashCall identifies the durable call IDs minted for commands the person ran. Live and reopened views use the same mark to show their output immediately.
func LandRunTree ¶ added in v0.4.0
func LandRunTree(dir, base, title, model string) (branch string, changed []string, refusal string, err error)
LandRunTree commits a run's own working copy onto the branch it is checked out on, and it is the landing half of the run engine (internal/run) stated here, where the commit road lives.
THE RUN'S WORK IS WHAT ITS TREE SAYS. Every worker of a run edits through bash (NewBeltWorker), and a shell worker fills no write ledger, so the working copy's status and commits since base record what the run made — the same road a bash-belt node's landing takes ([commitTaskWork] on its belt arm), reached by name rather than from inside a task tree.
IT ANSWERS FOUR THINGS AND ONLY THE FIRST THREE ARE THE LANDING. The branch is the one the copy stands on once the commit has landed. The paths are the net change since base, including a branch the worker merged in; when the net tree is unchanged but commits moved HEAD, they are the paths those commits touched. The refusal is the sentence a person reads when the landing had nothing to do or the tree would not take the work, and it is empty on every other road. The error is non-nil when the working copy or its history cannot be read or landed, rather than for an ordinary refusal to bring work home.
THE SWITCH IS THE BELT'S, read here for the reason NewBeltWorker reads it: the belt road stages the tree's own status minus what the harness itself writes, which is not what the default landing does, so a landing that ran with CODEAF_TASK_BELT naming the node belt would change a landing for one nobody composed. With the flag off this refuses and touches nothing.
ATTRIBUTION IS ON UNLESS CONTRIBUTING FORBIDS IT. The same two trailer lines are added to worker commits and the landing commit. model is the one the `Assisted-by` line names; empty leaves it bare because this door does not know which model each worker used.
func LastLook ¶
LastLook is when home was last closed over this places root, and zero when it never has been — or when the stamp is unreadable, which is the same fact for every caller: there is no origin to measure news from.
func LastLookAt ¶
LastLookAt is when this place was last left over this places root, and zero when it never has been — or when the file is unreadable, or when the place is not in it. All four are one fact for every caller: there is no origin to measure this place's news from, so nothing in it is news.
Places are plain strings — "home", "tasks", "standing", "memory", "spend", "search", "settings" — and NOT an enum here, because the list of places is the surface's to decide and a records package that held one would be the second place it was written down.
func LastTold ¶
LastTold is when this session's model was last handed the block, and zero when it never has been — or when the stamp is missing, unparsable, or from a schema this build does not know. Every one of those is the same fact for the caller: there is no origin, so the delivery is the bounded first one.
func LooseTasksRoot ¶
func LooseTasksRoot() string
LooseTasksRoot is the third place a node transcript can be: the parallel tree [taskJournalDir] writes into for a session that has no folder of its own.
A CONVERSATION WITH NO PLACE STILL RUNS TASKS, and its nodes' journals go here rather than under a project bucket that does not exist. It is a real, currently-written directory and not an archaeological one, so a record boundary that left it out refused a hosted room its own live transcript.
func MatchQuality ¶
MatchQuality is HOW WELL one query matches one piece of text, and whether it matches at all. It is the ladder above, and it is exported because it is the one fuzzy matcher in this program that more than one surface ranks with.
IT IS NOT [taskScore], AND THE DIFFERENCE IS DELIBERATE. That one ranks FIELD-MAJOR — every title-prefix beats every slug-prefix, which beats every substring — because an "@" mention is resolving one token to one task and the field it matched is most of the answer. This one ranks RUNG-MAJOR, because a person searching a whole machine cares how well the words matched and not which column they landed in; the caller weights the columns itself. Two orderings, one matcher underneath, and both of them say so.
text and needle are both expected lowercased; a caller folding case twice per row over a thousand rows is the one cost this refuses to pay for it.
func MusicDir ¶
MusicDir is AudioDir's own leaf rather than a share of it: two mp3s made by two different models for two different purposes are two kinds of deliverable, and a person listening back through a session's takes of a theme should not have to pick them out of its voiceovers (tools_music.go).
func NewMemoryTidy ¶
NewMemoryTidy is the seam a door fills standing.Ticker.Tidy with.
brainPath is the store's file and NOT an open store, which is the whole of how this stays cheap: a ticker is rebuilt every five minutes, and a handle opened per ticker would be a database connection per five minutes for the life of a window. The pass opens the store only once it has decided it is going to run — which is a few times a day — and closes it before it returns.
A BLANK brainPath IS MEMORY OFF and answers a nil seam, so the tick has nothing to call. That is the same absence Config.Memory being nil is on the turn path: the door opens no store when the memory row is off, and this reads the same switch through the same door.
func NewSessionID ¶
func NewSessionID() string
NewSessionID is 16 random hex characters: enough to name every session a machine will ever hold without a coordinator.
It is EXPORTED because the folder is named by it (place.go): the surface mints the id, makes the directory, and hands the same id back here for the header — one law for what a session id is, applied at both ends.
func NewStandingRunner ¶
NewStandingRunner is the seam a door fills standing.Ticker.Runner with. root is the store's own directory (standing.Store.Root).
func NewStandingSentinel ¶
NewStandingSentinel is the seam a door fills standing.Ticker.Sentinel with. It builds its client ONCE and lazily: a machine with no key, or a pass with no probe to judge, must not pay for a connection nobody used.
func NewsSubject ¶
NewsSubject is the name one piece of work's news is filed under: the conversation it is rooted in, and the node inside it.
IT IS EXPORTED BECAUSE BOTH SIDES OF THE SEAM SPELL IT, and there is exactly one spelling. The engine stamps it on every phase and every sighting a node produces ([Agent.newsSubject]); a surface asks its own desks for the subject of the room it has open (internal/tui3's phase.go and lanes.go). Two hand-written spellings of one identity is a room that quietly draws nothing forever, which is a defect no test would name.
THE CONVERSATION IS PART OF THE NAME BECAUSE NODE IDS RESTART. Every conversation counts its work from one, so `7` alone would alias — and a window CAN be looking at two conversations' node 7 at once, which is exactly what a guest room is (internal/tui3's taskGuest, whose own comment says a per-task key must be built this way for the same reason).
A CONVERSATION THAT CANNOT NAME ITSELF NAMES NOTHING. An empty conversation gives an empty subject, which reads as "the conversation" everywhere a subject is read — the honest answer, because a subject nobody can scope is a subject that would collide with somebody else's.
func NoteLook ¶
NoteLook records that somebody is looking at home right now. Errors are dropped: the stamp is a convenience over a screen that works without it, and a read-only disk must not turn closing a dashboard into a fault.
func NoteLookAt ¶
NoteLookAt records that somebody has just finished looking at one place. Errors are dropped for NoteLook's reason, and a zero instant writes nothing: stamping a place with the zero time would be indistinguishable from never having stood in it.
func NoteTold ¶
NoteTold records that the block has gone in front of this session's model.
Errors are dropped, on NoteLook's reasoning: the stamp is a convenience over a conversation that works without it, and a read-only disk must not turn a turn into a fault. A TORN WRITE COSTS ONE REPEAT — an unparsable stamp reads as no stamp, and the next delivery is the bounded first one again, which is the harmless direction to fail in.
IT ONLY EVER MOVES FORWARD. Two readings can be in flight over one conversation — the second turn's starts while the first turn's is still walking the index — and they are written behind the path ([Agent.toldStamp]), so the order they LAND in is not the order they were taken in. An older stamp landing last would re-read landings the model has already been told about, which [deltaRemember] would then de-duplicate away: news that is never told. Reading before writing costs one open of a file this call has to open anyway.
func NothingToCompactWhy ¶
NothingToCompactWhy reads the reason out of a no-op, and reports false for any other error. It reads the words as well as the type, because a remote engine's error reaches a surface as its text alone.
func OnLaneNews ¶
OnLaneNews registers the reader every answer's lane story is told to, and hands back the one that was there — so a surface that opens over another can put it back when it closes. A nil function unregisters. A sighting told while nobody was registered is handed to the reader as it registers ([laneNewsHeld]).
func OnPhaseNews ¶
OnPhaseNews registers the reader every phase change is told to and hands back the one that was there. A nil function unregisters, and takes this package's forwarder off the transport with it.
func OpenRunPlan ¶ added in v0.4.0
OpenRunPlan opens the older flat-layout plan path, seeded with the run's words. The headless do door now uses OpenRunPlanAt in its private folder. A store already at that path is SET ASIDE beside it first ([setAsideRunStore]) — a finished one as it ended, and one nothing is driving as interrupted — and a fresh one is seeded, so a second errand in one project is a second run rather than a reader of the first one's ending. The store is the caller's to close.
A NEW ERRAND NEVER ADOPTS A RUN IT DID NOT START. This door used to adopt a store whose root was still open, on the reading that an open root was a live run to resume. Nothing resumes through this door: every call carries a new request's words, and a store left open by a run that was interrupted — a timeout, an interrupt, a process that died — was run again under its old title and brief while the new request was dropped. Measured on this door: a directory holding a left-open store answered `status=running title="rename the logger"` for a request about something else entirely.
func OpenRunPlanAt ¶
OpenRunPlanAt seeds one run at an explicit store path. A headless run puts this path in its private home, leaving the working copy for the work alone.
func ParseReasoning ¶
ParseReasoning normalizes one operator-supplied level and reports whether it is one. It answers in the surface's own words so a door can validate `--reasoning` without importing the adapter, and "off" and "" both normalize to "" — see the block comment above for why off is silence and not provider.EffortOff.
It is effort.Parse under a name the doors already call, so the five rungs a person may type here are the same five the ladder holds and there is no second list to keep true.
func PartialString ¶
PartialString reads ONE named top-level string field out of the raw, partial arguments of a forming call — Event.ArgsText — and answers with everything of it that has arrived so far, closed or not, plus whether the field was seen at all.
It is the door a surface goes through to draw a write's body while the body is still arriving, and it exists so that door is the SAME SCANNER the hints are built from. ArgsText is half a JSON object; json.Unmarshal answers "this is not valid JSON" about every prefix of one, so a surface that wanted the text had exactly two options — a second tolerant parser of its own, or this. The scanner below already handles the two cases that make a prefix hard: a string that has no closing quote yet, and a text that ends in the middle of an escape (`\` alone, or three of the four hex digits of a `\u`), both of which simply contribute nothing until the rest of them lands.
What comes back is the DECODED value — `\n` is a newline here — capped at PartialStringLimit bytes taken from the END, because the caller drawing this is drawing the last few lines of a file that is still growing. It cannot fail and it never returns an error: the worst input produces an empty answer.
func PatchSectionPath ¶
PatchSectionPath is the path one section is about, as the file stands now.
A rename or copy says its new name on a line of its own (`rename to`, `copy to`), which is never ambiguous, so that line wins. Otherwise the `diff --git` line names the file twice. When git quoted it, the second quoted token is the name. When it did not, the two halves are the same name, so the line is measured rather than split: `a/P b/P` is two plus P, three, and P again, which finds P even when P itself holds ` b/`.
func PatchSections ¶
PatchSections splits a unified patch into one piece per file, each starting at its `diff --git` line, so a file's header travels with its body.
func PeekReport ¶
PeekReport is the LAST THING THE AGENT SAID in a journal, read off the file and whole.
IT IS THE SENTENCE THE PROJECT'S RECORD ALREADY QUOTES THE FIRST LINE OF. A node's report is its final assistant message (task_run.go's lastSaid), and TaskIndexEntry.Outcome is that message's first sentence, cut to [taskOutcomeLimit]. So a surface holding the row and wanting the rest of it follows the row's own TranscriptURI through here rather than keeping a second copy — the index carries citations, and this is what following one costs.
It is Peek's discipline and not [openSessionFile]'s: open, scan forward, close. No lock, no replay, no repair, nothing created. A journal that is not there, cannot be read, or never had the agent say anything answers false, which is a surface drawing nothing rather than a surface drawing an empty quotation.
func PlaceSession ¶
PlaceSession is the session id a folder names. A session folder is named for its session (Decision 26, and Meta.ID says the same), so the id is the folder's own name and no file has to be opened to learn it.
A session with no folder answers NOTHING rather than a guess made out of a file name — an artifact row's session is a citation, and a citation nobody can verify is worse than a row without one (design-law §EMPTINESS).
func PlacesRoot ¶
func PlacesRoot() string
PlacesRoot is where every project's bucket lives under the state root. It is SweepHome's root, exported because the sweep is no longer the only caller that wants the whole machine — the same directory, asked a different question.
func PlanStorePath ¶ added in v0.4.0
PlanStorePath is the flat-layout fallback for a session with no Place: <dir>/.codeaf/plandb.db. A new headless run uses OpenRunPlanAt in a private folder instead; this path remains for the session fallback.
func PreflightNote ¶
PreflightNote is the ONE LINE a proposal carries about the work other windows already have out, or "" when there is nothing to say.
It is pure and takes the reading it judges, so a surface holding a cached Elsewhere does not go back to the disk for this (internal/tui3 holds one on a three-second clock). root is the workspace the paths in parts would be relative to, and "" simply means an absolute path in the brief cannot be placed and is dropped.
parts are the pieces of the brief to read paths out of — title, summary, the work itself, the deliverable, the acceptance — passed separately because they arrive separately and joining them here would be one more spelling of the same text.
func ProgramBadge ¶
ProgramBadge is a program's name as the badge its work wears everywhere a task is named — `[senior-dev]` — and it is the ONE spelling of the brackets (internal/tui3's programbadge.go draws its full spelling from this).
THE BRACKETS ARE THE BADGE, NOT DECORATION. A surface paints the badge in its own ink, and a terminal with no colour, a selected row whose ground swallows a tint and a sentence read aloud have only the brackets left to say that the word inside them is a program's name rather than part of the title.
func ProgramShellModelRefusal ¶
ProgramShellModelRefusal is the shell's one sentence for a model it cannot hand to a child, including a model named before any service key is present.
func ReadRows ¶
func ReadRows(transcripts []string) map[string]SessionRow
ReadRows is the rows of the named conversations and of nothing else, keyed by each transcript's cleaned path: one [readSessionRow] per name, read exactly as the walk reads that folder.
IT IS FOR A SURFACE THAT KNOWS WHICH CONVERSATIONS IT DRAWS. The teams page draws its members, twenty-five on a big machine, and it used to walk every session under the root on its opening and on every beat to find them: a stat, a meta.json, a presence file and a lock taken and let go for each of hundreds of folders, to keep a handful. A name that is not a session folder's journal, or whose folder is not a conversation somebody has had, is simply absent.
THE TASK ROLL-UP IS NOT READ. The index is the bucket's (TaskIndexPath's law) and nothing that asks for rows by name draws it, so SessionRow.Tasks is the zero roll-up here, as are the project fields.
func ReasoningKey ¶
ReasoningKey folds a model id the way the level map is keyed. It is exported because a REPLICA of that map lives on another machine (internal/remote) and two spellings of one folding rule is a level that is set under one key and looked up under another.
func RecordArtifact ¶
RecordArtifact writes one row. Every failure is silence, for appendTaskIndex's reason: the caller has just produced real work, and there is nothing it could usefully do with the news that a lookup file could not be written. A row with no path or no title is not a citation and is not written.
func RecordRoots ¶
func RecordRoots() []string
RecordRoots is every directory this machine's task transcripts live under, and it is what an ENGINE measures a record read against (ReadTaskRecordUnder).
THREE ROOTS BECAUSE THE RECORD IS IN THREE PLACES. An ordinary task writes its journal beside the conversation that ran it, under the places root; a conversation with no folder writes its nodes' journals into the parallel tree instead; and an adaptive run's nodes write theirs under the runs root. Every one of those rows is handed to a surface on the same world walk, so a door that admitted only the first told a person over a connection that most of their own machine's work "could not be read" — the file was there, and the boundary was wrong.
func RecordUnbilledCall ¶
RecordUnbilledCall keeps the missing receipt on disk with its owner. The marker carries no invented money or token count and survives process restart.
func RecordUsage ¶
RecordUsage queues one line. Every failure is silence, for RecordArtifact's reason: the caller has just finished a piece of a person's turn, and there is nothing it could usefully do with the news that a spending record could not be written — least of all tell them about it mid-sentence.
A LINE THAT SPENT NOTHING IS NOT WRITTEN, which is [sessionFile.appendUsage]'s own guard kept here as well rather than trusted: this file is appended to from more than one door over its life, and a zero row in a spending ledger is worse than no row — it is a day that looks measured and was not.
func RegisterRunEngine ¶ added in v0.4.0
func RegisterRunEngine(engine RunEngine)
RegisterRunEngine installs the run engine the chat's task door reaches. It is called by the engine's own package at load, so a binary that links the engine gets the run road and one that does not gets the legacy one.
func ResolveProgramShellModels ¶
func ResolveProgramShellModels(words string, available []string, sources modelsource.Set) (string, error)
ResolveProgramShellModels gives a shell program the same model-word matcher and service check as a chat proposal. A shell has no card for a shortlist, so it asks for a more precise word before starting the child.
func RunsRoot ¶
func RunsRoot() string
RunsRoot is where an adaptive run's node transcripts live under the same state root — the directory [orchestrateJournalPath] writes into.
IT IS A SIBLING OF THE PLACES ROOT AND NOT A CHILD OF IT, which is the whole reason this name exists. A run's rows are filed in a project's index like every other piece of work, and the transcript each row points at is over here; a door that took the places root for the machine's whole record therefore refused every adaptive journal it had itself written down (RecordRoots is the fix).
func SaveMeta ¶
SaveMeta writes the identity whole, temp-and-rename, never partially: a picker that reads a half-written meta.json would draw a phantom row.
func SeatValid ¶ added in v0.3.0
SeatValid reports whether a word is one of the seven. It compares EXACTLY: no trimming, no case folding. A word from outside the set is not a seat and is never written to a row — TagUsage drops it rather than writing a guess.
func ServesModel ¶
func ServesModel(sources modelsource.Set, model string) bool
ServesModel answers whether one of these services can take a call on model: the service the model's id resolves to (modelsource.Set.For, which reads a service prefix such as `openrouter/` off the id) holds a key, or is one that needs none. It is the pool's own test ([modelServiceCanAnswer]) opened to the run's model API (internal/provider/modelapi), which decides the same question for a program's call and may not answer it a second way: a program that names a model this machine cannot reach is answered on the run's work seat instead, and the pool and the API must agree about what cannot be reached.
func SetArchived ¶
SetArchived marks or unmarks one conversation as put away, through the same meta file every other fact about the session rides. A folder with no conversation in it is refused rather than given a meta that claims one.
func SetOfferAnswerer ¶
func SetOfferAnswerer(answerer offerAnswerer) (previous offerAnswerer)
SetOfferAnswerer installs the side that owns the open offers, and hands back the one that was there. A nil answerer uninstalls it, which is the state a build with no transport wired is in.
func SetProgramAnswerAttribution ¶
func SetProgramAnswerAttribution(folder *ProgramFolder, named bool) error
SetProgramAnswerAttribution credits the models in the loopback call record that answered, rather than the crew selected before the run began.
func SetRunningCLI ¶
func SetRunningCLI(path string)
SetRunningCLI registers the path of the running codeaf binary — the one the person started, under whatever file name it was installed (codeaf, devaf, stageaf, a `--name` word). cmd/codeaf calls it once, at start.
func SetTaskArchived ¶ added in v0.4.0
SetTaskArchived changes one task's visibility under the conversation's metadata lock. It never changes the task index, execution state, or conversation archive.
func SetTeamResume ¶
SetTeamResume gives this process a way to open a team conversation that no window and no host holds, by its transcript and its folder, so the traffic that should wake it can. cmd/codeaf's engine sets it to a hello to that folder's session host, which opens the conversation and keeps it running with no surface attached; a window that opens it later joins that one. nil takes the door away.
func SharedFiles ¶
SharedFiles is the paths two sets of files have in common, in the order the FIRST set names them, with anything blank and anything repeated dropped.
It is the whole of the comparison, and it is one function so that no surface has to spell a nested loop over two path lists again. Either side being empty answers nothing at all — see this file's second law for why a caller must not read that as "these do not overlap".
func ShortCaption ¶
ShortCaption keeps ONE short sentence. Prefer a complete sentence under the word budget; if a longer sentence must shrink, drop trailing dangling words so the title does not end mid-clause ("… which are"). It is exported because the surface draws step captions with the same rule the narrator uses; keeping a copy in both packages is how the dot bug came to need fixing twice.
func SpendShare ¶
SpendShare is how much of a daily budget usd is, as a fraction: 0.5 is half of it. It is 0 when there is no budget — config's 0 is no ceiling at all (internal/config's DailyBudgetUSDAt), and a share of no ceiling is nothing a surface should draw.
IT IS NOT CAPPED AT ONE. A day over its ceiling is news, and a surface drawing a bar clamps it there itself; clamping here would hide the overrun from every other reader.
The budget is an ARGUMENT and not read here, so this file stays what its header says it is: arithmetic over what a page already has.
func SpendToday ¶
SpendToday is what was spent on now's local calendar day, in dollars — the figure a daily ceiling is measured against.
THE DAY IS THE WRITER'S, through UsageLineDay, so this and the day's bar in UsageByDay are one answer: a figure beside the bar that re-derived the day from each row's timestamp would disagree with it for every call made near midnight in another zone.
func SpokeIn ¶
SpokeIn answers whether anybody ever spoke in one transcript — and, second, whether that answer is one to act on.
Peek cannot be asked this. Its boolean folds four different files into one false: a journal that is missing, one that could not be opened, one whose lines would not parse, and one that was read from end to end and holds no turn. A picker is right to treat all four alike, because all four draw the same empty row. A CALLER THAT DELETES THE FOLDER IS NOT: the last of those is litter and the first three are somebody's conversation seen through a reader that failed.
So this is the third answer. sure is true only when the file itself settled the question:
- the journal is not there at all — absence is a fact, and the fact is that nothing was ever written;
- or every non-empty line in it parsed, every line named a kind this build knows, and the scan reached the end.
Anything else — a directory in the way, a permission, a torn last line from a lid closing mid-write, one line past the scanner's buffer, a kind written by a schema this build has never seen — leaves sure false, and a caller that destroys on silence must read that as "do not".
func StandingChangeHint ¶
StandingChangeHint is what the correction button does, in one sentence.
func StandingChangeWord ¶
StandingChangeWord is the correction button. A rule's correction is about where it reaches. Everything else is about the arrangement.
func StandingHead ¶
StandingHead is the card's first line for this item.
func StandingIdle ¶
StandingIdle is the seam a door fills standing.Ticker.Idle with: the world reader's answer to "has nobody been here for a while".
TWO CONDITIONS, AND BOTH ARE ABOUT PEOPLE. Nothing may be working right now — a machine mid-build is not idle however long ago somebody typed — and the newest thing anybody SAID anywhere must be older than the span. The second is read from Meta.LastUserAt and deliberately not from file times, which is the ordering law everywhere in this codebase: a background write is not a person returning to a conversation.
func StandingOnceIsAnAnswer ¶
StandingOnceIsAnAnswer reports whether "do it once, now" MEANS anything for one item, and it is the ONE PLACE that is decided.
"Once" means: do the action NOW, as an ordinary turn, and leave nothing behind (StandingAnswer.Once). A repeating check and a watch are things a person may want done once. A reminder's whole content is the moment, and doing it now says the thing at the wrong time. A rule never runs, so "once" has nothing to do.
func StandingPlainLabel ¶
StandingPlainLabel is a label with its cadence removed. A label that carries none is returned as it is.
A REMINDER'S WHEN IS THE WHOLE TAIL, including a clock joined on with the same mark a repeating check uses between its verb and its cadence. "Remind me in 1 minute · 07:35" drops to "Remind me", not to "Remind me in 1 minute": the mark inside the when is part of the when, and a narrow row still loses the when before it cuts a character.
func StandingWorld ¶
StandingWorld renders the section the person's orders put into the world of work being born, or "" when nothing stands over the place or the ambient side is off. It is the exported door on the birth seam for the callers outside the package that are seated at the same moment a node is — a run's door handing its workers a brief — and it is ONE CALL to standing.Store.Applicable with the caller's place, because which orders govern a place is the resolver's question and nobody else's (this file's header).
func SteerDelivered ¶
SteerDelivered is the ONE FACT about what sending a line to a node did, and it is authored HERE — by the engine that did the sending — rather than by whichever surface happens to be drawing the page.
THE SURFACE DRAWS IT VERBATIM AND NEVER ITS OWN WORD FOR IT, which is why this is exported: the room draws the clause the instant Agent.SteerTask answers, and the record keeps the same sentence for whoever opens the page tomorrow. A surface with its own spelling would be a second author of one fact, and the live page and the replayed page would quietly stop agreeing.
waiting is Agent.SteerTask's own second answer: the node had handed its work out and parked on the reports, so this line is what wakes it.
func StopHolder ¶
StopHolder asks the process holding the conversation in sessionDir to stop. The first call sends SIGTERM — the ordinary leaving road, which closes every conversation that window holds and flushes its journals; a second call, when the first did not free the lock, sends the second one, which every build since the leave road answers by exiting at once.
IT SIGNALS ONLY A PROCESS IT CAN NAME AND THAT IS STILL HOLDING THE LOCK. The pid comes from the holder's own presence record, the journal's flock must still be held (a free lock is a holder that already went), and the process asking is never its own target. It answers the pid it signalled.
func StopUsageWriter ¶ added in v0.4.0
StopUsageWriter ends ONE path's writer and waits for it, the same detach and join CloseUsage performs for every writer at once, for a caller that is finished with one ledger while the process goes on. A test that made its ledger a stalled path is the caller that needs it: its directory is about to be removed, and a writer still holding rows for that path would rebuild it.
It answers true when the writer is really finished. False is the same bargain the rest of this file states: a writer parked inside [openUsageLedger] on a path that never answers is left behind rather than held onto, because a spending record is worth less than the exit.
func SubharnessInput ¶
func SubharnessInput(card SubharnessCard) json.RawMessage
SubharnessInput is the card as the runner takes it: one JSON object of the fields that have a value.
IT IS THE ONE PLACE A CARD BECOMES AN INPUT, so a surface that lets somebody edit a field inline and a surface that just confirms what was inferred are launching the same object. A field with no value is LEFT OUT rather than sent as null — a program's optional field being absent and being explicitly nothing are two different instructions, and only one of them was given.
func SummarySkippedWhy ¶
SummarySkippedWhy also reads remote errors that carry only their text.
func SweepHome ¶
SweepHome runs one pass over this machine's session folders and one over the ambient side's own. It is the launch door's call (SweepPlaces and SweepStanding are the testable ones underneath it).
standingRoot is passed in rather than resolved here for the reason every other root in this file is: a pass that computed its own paths could not be pointed at a temp directory and therefore could not be proved. An empty root is a build with no ambient side, and rule 4 does nothing at all.
func SweepHomeContext ¶ added in v0.4.0
SweepHomeContext is SweepHome with cancellation. Cancellation is checked before resolving the ambient home so a stopped launch cannot target a home selected after it started.
func SweepPlaces ¶
SweepPlaces applies the three rules over one projects root.
A root that is not there is not a failure — it is a machine that has not held a conversation yet — and answers silently.
func SweepPlacesContext ¶ added in v0.4.0
SweepPlacesContext is SweepPlaces with cancellation between entries and immediately before each destructive operation.
func SweepStanding ¶
SweepStanding applies rule 4 over one standing root: the errands that came to nothing, and the runs that delivered nothing.
EVERY PATH IT TOUCHES IS ARITHMETIC ON root, and there are exactly two of them — <root>/exchanges/<id>/ and <root>/<item id>/runs/<n>/. It cannot reach v3/projects because it never reads that directory, and an empty or missing root answers silently: a machine with nothing standing has nothing here.
func SweepStandingContext ¶ added in v0.4.0
SweepStandingContext is SweepStanding with cancellation between entries and immediately before each removal.
func TakeoverPath ¶
TakeoverPath is where a request for the session in dir is written.
func TaskAgeWord ¶
TaskAgeWord spells how long ago, coarsely, in the one unit that matters at that distance. It is exported because the "@" drop-up and the pointer block (internal/tui3) say the same ages about the same rows, and two spellings of "3h" on one screen is two vocabularies for one fact.
func TaskIndexPath ¶
TaskIndexPath is the project's index for one session file, or "" for a session with no file at all — a memory-only conversation has no project directory to keep a project's record in.
A SESSION FOLDER'S INDEX IS ITS BUCKET'S. A journal called transcript.jsonl is a folder's (place.go), the folder is one conversation, and the project is the directory ABOVE it — so the index climbs one level rather than landing inside a single conversation, where every window would keep a private list of the same project's work. A legacy flat transcript keeps the index beside it, which for that layout is the same directory.
func TaskKindWord ¶
TaskKindWord is what a person reads where the code holds a TaskKind, and it is the ONE place that translation is made — [taskStateWord]'s law applied to the other half of a row.
Ordinary work has NO WORD, and that is the emptiness law rather than an oversight: "plain" on a row would be a machine telling somebody that the task they asked for was a task. The word only ever earns its place by naming a kind of work that behaves differently — a run that plans itself, a saved shape being run or being made, a command running in the background.
The vocabulary is the surface's own, taken from where a person already meets each thing: `adaptive` is what `/task adaptive` and the manual's adaptive-runs.md call it, and `saved shape` is what saved-shapes-of-work.md calls a subharness. No machinery word — "subharness", "harness", "node" — reaches a screen through here.
func TaskMatches ¶
func TaskMatches(entry TaskIndexEntry, query string) bool
TaskMatches reports whether one row of the index answers a query at all.
IT IS THE SAME LADDER SearchTaskIndex RANKS WITH, asked for the yes and not for the place ([taskScore] is the one implementation of both). A reader that wants the index FILTERED rather than RANKED — the task page keeps the record in the order it happened and merely takes rows out of it — would otherwise have to spell the ladder out a second time, and two spellings of "does this task match" is two lists that disagree about which tasks exist.
AN EMPTY QUERY MATCHES EVERYTHING, which is the other half of the bargain SearchTaskIndex makes with one: nothing typed is not a filter that excludes everything, it is no filter at all.
func TaskProposalHead ¶
func TaskProposalHead(notice TaskNotice) string
TaskProposalHead is the whole sentence a proposal asks with: TaskProposalLead and the title, and — for work going to a program codeaf carries — the program's badge where the word `task` is, so the question reads `wants to start a [senior-dev] task: <title>`.
THE PROGRAM IS IN THE SENTENCE AND NOT ONLY ON THE CARD, because the sentence is what every reader of a proposal gets: the block above the box, home's needs panel, a surface over `--host`, and a plain-text or screen-reader reader that draws no card at all. A person approving work is owed who it is going to wherever they approve it. The badge stands before the title rather than after it because a narrow reader cuts a head from its end.
IT IS EXPORTED FOR TaskProposalLead's REASON: the surface builds the same question from the same notice, and two builders that drifted would be two questions about one proposal.
func TaskReasonOf ¶
func TaskReasonOf(ending TaskEnding, report string) string
TaskReasonOf is the incomplete reason sentence for one ending, in the person's own words, and it is the ONE place that table is written down.
The report is read for the three endings whose reason is not knowable from the word alone: a check that named gaps, whose finding is the first line of its own report, a program's own ending, whose sentence is that first line, and a fault, whose first line is the only account of what broke. A stop is not here at all — `stopped` is its own word, not a kind of incomplete.
func TaskRecordPath ¶
TaskRecordPath is the LOCAL FILE a row's URI names, or "" for a URI that names anything else.
The record carries two URIs and one of them is sometimes not a file at all: a node whose worktree was pruned keeps its branch, spelled `git:task/…` ([taskArtifactURI]). A file URI naming a host names another machine's disk, which is the same refusal the surface's path linker makes about one. Everything this returns is a path the machine that WROTE the URI can be asked to open.
func TaskReportAccount ¶
TaskReportAccount removes only the exact answer suffix the runner composed into a report. Expanded readers already show that answer in full; retaining its bounded preview would repeat it above the answer in diagnostic ink. Older records used a plain three-line preview, which remains recognizable. Unmatched reports remain intact, including accounts rewritten after landing.
func TaskResolveEnum ¶
func TaskResolveEnum() string
TaskResolveEnum is the same list as the JSON array a tool schema takes.
func TaskResolveVerbs ¶
func TaskResolveVerbs() string
TaskResolveVerbs is that list as it reads inside a sentence — `accept|reaudit|refute` — which is how the landing note offers it.
func TaskSlug ¶
TaskSlug is a title as an "@" token: lower case, words joined by hyphens, everything that is not a letter or a digit dropped.
It is the mention's whole addressing scheme, and it is derived rather than stored-and-minted because a person typing "@fix-the-nil-map" is typing what they can SEE — the title — and any scheme that gave the task a name they could not derive from its title would be a name they have to look up first.
func TaskWordsMatch ¶
TaskWordsMatch is the same question asked of WORDS rather than of a row: does this title answer this query.
It exists because a task that is still running in THIS conversation is a node of a live graph and not a row of the file — the page filters both halves of itself with one query, and a live node has a title where a row has six fields. What it shares with [taskScore] is the part that is about the words: the substring first, then the subsequence, so that "prsr" finds "Port the parser" on both halves of the page or on neither.
func TeamWakeNote ¶
TeamWakeNote reports whether text is a note the session wrote to wake a turn on team traffic.
func TellLane ¶
func TellLane(news LaneNews)
TellLane is TellPhase's twin for the lane sighting: which machine answered, how fast it wrote, and whether a rescue went out while somebody was waiting. A news with no model belongs to nobody and [postLaneNews] drops it, which is the same law this side of the wire as the other.
func TellPhase ¶
func TellPhase(news PhaseNews)
TellPhase hands a phase that was measured on ANOTHER MACHINE to this process's registered reader, exactly as if it had been measured here.
IT IS FOR ONE CALLER: the surface's side of a connection to an engine host (internal/remote), which decodes a "phase" frame and calls this. It runs [forwardPhase] — the same forwarding the in-process road runs — so a relayed phase lands on the same desk, wakes the same repaint, and is aged out by the same window as one measured here.
IT CANNOT DOUBLE-POST. A build where the engine and the surface share a process has no connection between them, so nothing ever calls this; a build where they do not share a process has two readers in two processes and each posts once. The [PhaseNews.Relayed] stamp the caller sets is what keeps a build that is BOTH — a test driving a host inside its own process — from sending back out what it just took in.
func TrustedWindow ¶
TrustedWindow returns a catalog window without guessing an endpoint. The provider applies endpoint-specific evidence to each assembled request.
func TrustedWindowFor ¶
TrustedWindowFor preserves the surface API, but a model-only memo is no longer a context limit. Old served_window entries were rejected prompt sizes.
func UnattendedNotice ¶
UnattendedNotice is the one line a door shows about the ceilings this launch was given, or "" when there is nothing worth saying. A steered conversation states its budget rather than silence, so the pair it was given is never mistaken for a goal owner.
func UnbilledCalls ¶
func UnbilledCalls() int64
UnbilledCalls is how many calls this process knows the provider charged for and could not put a figure on. Zero is the ordinary answer and is rendered as nothing; a nonzero answer says every ledger total may be short by those calls.
func UnsavedEditsNote ¶
UnsavedEditsNote is the ONE LINE a task start carries about the person's own uncommitted changes, or "" when there is nothing to say.
THE EXPECTATION IT CORRECTS is what somebody is about to hand out. A task's world is the folder AS IT STANDS, half-finished edits and all, so a person with something experimental open should know it is going with the work at the one moment when committing, stashing or waiting is still cheap.
IT IS A NOTE, NEVER A GATE. Nothing here blocks, waits or asks; the caller hands the work over on the very next statement. And it is silent by default — a clean tree, a directory that is no repository, a `git` that is not there all answer "" rather than "no unsaved edits", because the emptiness law is that nothing true and uninteresting gets a line of its own.
ONE FORK, AT THE START. This shells out, so it belongs at a task's start and nowhere on a frame's road. Untracked files are deliberately NOT counted (`--untracked-files=no`): they travel with the task exactly as tracked edits do, but build output and scratch files make nearly every repository permanently untracked-dirty, and a line that fired on every start is a line nobody reads by Tuesday — the same reason [hotFiles] exists.
func UsageDrops ¶
func UsageDrops() int64
UsageDrops is how many spending records this process failed to write down. Zero is the ordinary answer and a surface says nothing about it; anything else means every total taken off a ledger is short by that many calls.
func UsageLedgerPath ¶
func UsageLedgerPath() string
UsageLedgerPath is this machine's ledger, resolved through internal/home so CODEAF_HOME moves it with everything else (Decision 26 — one home, one seam). It is the fallback the engine writes to; a caller with a path of its own — a test, a second brain on one laptop — hands one to RecordUsage instead.
func UsageLineDay ¶
UsageLineDay is the first local moment of the day a row belongs to: the day the WRITER wrote down, parsed as a local date, and the row's own timestamp where it says nothing.
It is one function rather than an expression at each site so that the window test and the bucket search in UsageByDay cannot come to disagree about which day a row is on — which would silently drop a row into no bucket at all. It is exported so page-level window filters use that same writer-recorded day.
func UsageSubjectWord ¶
UsageSubjectWord is the word a row wears for what money went on, and it is the one place those three words are spelled — TaskKindWord's law applied to the other axis of a spend row. An unknown kind reads as nothing rather than as itself, because a machine word leaking onto a screen is the failure this function exists to prevent.
THEY ARE COLUMN WORDS AND SO THEY ARE SHORT. They were `a task` and `a conversation`, which are how a SENTENCE names those things and twice what a column needs: the spend place stands them in a column of their own beside the project and the figure, where the article is a cell spent saying nothing and `conversation` is twelve of them. `chat` is what this surface already counts them in — the tasks place's own head row says `3 chats · 8 subtasks` — and the long spelling stays where it belongs, in prose (home's `start a new conversation`).
func UserFacingUpdate ¶
UserFacingUpdate recognizes only a leading protocol marker, never a quoted mention or a marker in the middle of an answer. Unmarked content is unchanged.
func WebFetch ¶ added in v0.4.0
WebFetch is the belt's web_fetch road, shared with the command line's web door for the same reason: one call, markup stripped, bounded, the same words whichever door asked.
func WebSearch ¶ added in v0.4.0
func WebSearch(ctx context.Context, provider search.Provider, query string, count int) (string, bool)
WebSearch is the search road the belt's web_search runs, as a plain function the command line's web door runs too: one call, one failure sentence or one rendered list, in exactly the tool's own words.
func WebSearchCount ¶ added in v0.4.0
WebSearchCount clamps a caller's ask exactly as the tool clamps the model's: an absent ask is the default; a zero, a negative or an over-ask is corrected silently rather than refused.
func WithOwnCacheLineage ¶
WithOwnCacheLineage marks a call whose context already carries the cache key its request must travel under, so the conversation's wrapper keeps that key rather than stamping its own.
IT EXISTS FOR ONE CALLER AND IT IS OPT-IN. A program codeaf carries talks to its model through the run's model API (internal/provider/modelapi), which hands each call to this conversation's completer — and the program keeps conversations of its own, each with its own `prompt_cache_key`. Stamped with the conversation's key, every one of them would ask for the conversation's warm instance: two different prefixes on one lineage, each cold-starting the other, which is [unwrapCompleter]'s reason for giving a task node a lineage of its own. A call that is not marked keeps exactly the stamp it always had.
func WorkPath ¶
WorkPath is the little a surface needs to walk from an id back to the work: the kind word and the rest. It is the inverse of the spellings Agent.WorkingNow mints, written here so a reader never has to take the format apart with its own strings.Cut.
func WriteAnswer ¶
func WriteAnswer(sessionDir string, kind QuestionKind, id uint64, key string) error
WriteAnswer leaves one answer on a session's doorstep.
It is the seam a surface is handed (tui3's Options.Answer), and it takes the session's FOLDER rather than an agent, because the whole point is that the session being answered is in another process. A key the kind does not take is refused here rather than written and dropped later — the surface that offered the chip is the one that can still say something about it.
THE REFUSAL IS THE KIND'S OWN LIST WHERE THERE IS ONE, AND THE QUESTION'S EVERYWHERE ELSE. AnswerOptions answers for the eight lanes whose keys are fixed by the lane rather than by what is being asked; for the rest — the model's own `ask`, a running sub-harness, the stuck-turn question — THE QUESTION CARRIES ITS OWN OPTIONS (PresenceQuestion.Options, written by the session that is waiting) and this table has nothing to say about them. It used to refuse them anyway, so every answer given from home to a question the model raised came back `could not leave that answer — open the conversation and answer it there`: the chips were drawn off the question's own options, the key was checked against them, and then this door threw it away. A surface has already asked PresenceQuestion.Label before it reaches here, which is the narrower list and the honest one.
Types ¶
type AbandonReason ¶
type AbandonReason string
AbandonReason is why a turn was let go of, in the words the journal keeps. There is one today; it is a named type so the file's reader can tell a bound that fired from whatever else may come to use this door.
const AbandonStopTimeout AbandonReason = "stop timeout"
AbandonStopTimeout is the bound: a person stopped the turn and the engine had not let go of it by the time the surface's deadline came round.
type ActionCategory ¶
type ActionCategory string
ActionCategory is the family of work one step belongs to. It is a string rather than an integer because it travels on the wire beside the caption, and a number would have made an old surface's "3" mean whatever the thirteenth constant became.
const ( // ActionSearch is looking for something whose location is not yet known. ActionSearch ActionCategory = "search" // ActionRead is opening something already located, and listing what is // there — the two are one act to a person watching. ActionRead ActionCategory = "read" // ActionEdit is changing something that already exists. ActionEdit ActionCategory = "edit" // ActionCreate is writing content or generating a new artifact. ActionCreate ActionCategory = "create" // ActionRun is executing a command and waiting for what it prints. ActionRun ActionCategory = "run" // ActionTest is checking work that has been done. IT HAS NO DEFAULT TOOL and // only the narrator ever picks it: a suite is `bash` and so is everything // else `bash` does, so the tool name cannot tell them apart. That is honest // — the fallback for a shell call is [ActionRun], which is true of a test // run as well. ActionTest ActionCategory = "test" // ActionBrowse is going out to a page or a service and reading what is // there. ActionBrowse ActionCategory = "browse" // ActionTransfer is moving bytes or state from one place to another. ActionTransfer ActionCategory = "transfer" // ActionCommunicate is saying something to a PERSON — a message, a mail, a // spoken line. ActionCommunicate ActionCategory = "communicate" // ActionCoordinate is work handed out or copied to run beside this one. ActionCoordinate ActionCategory = "coordinate" // ActionPlan is keeping the account of the work rather than doing it. ActionPlan ActionCategory = "plan" // ActionWait is standing by for something outside this turn. ActionWait ActionCategory = "wait" // ActionWork is the honest bucket: a hand this build does not know, a // service armed at runtime, a narrator that said nothing usable. It is a // real answer rather than an absence, so the gutter never goes blank and // never jumps. ActionWork ActionCategory = "work" )
The thirteen families. THE ORDER IS THE ORDER THE MODEL IS SHOWN THEM in ActionCategoryWords, so the list a person reads in the manual, the list the prompt spells and the list the parser accepts are one list.
Each is a VERB A PERSON WOULD USE about work, never a machinery word: the families are what somebody watching would say is going on, which is the same bar every caption on this surface is held to.
func ActionCategories ¶
func ActionCategories() []ActionCategory
ActionCategories returns every family in prompt order.
IT IS A COPY, and the copy is load-bearing rather than tidy. The prompt is built ONCE, at package initialization, out of this same table; a caller handed the backing array could write into it — a test tightening the list, a surface sorting it for a menu — and from then on the parser would accept a vocabulary the model was never shown, and refuse the one it was. A capped slice prevents an append from reaching the array and does not prevent that, so this allocates. It is called at construction time and never on a frame.
func ActionCategoryForTool ¶
func ActionCategoryForTool(tool string) ActionCategory
ActionCategoryForTool is THE DETERMINISTIC FLOOR: the family of one call, from its registered name alone.
IT IS EXHAUSTIVE OVER THE BELT THIS BUILD CARRIES, and a test walks the belt to say so — a hand that landed without a family here would draw the generic mark forever and nothing would report it. Everything else answers ActionWork: a tool armed at runtime by a connected service has a name this binary has never seen, and a bucket is the honest answer to a name rather than a guess spelled out of its substrings.
It takes the name and nothing else. The arguments are not read, deliberately: a shell call that runs a suite is ActionRun here and ActionTest only when the narrator says so, because a second rule about `go test` living in a second package is exactly the drift this file exists to prevent.
func ActionCategoryForTools ¶
func ActionCategoryForTools(tools []string) ActionCategory
ActionCategoryForTools is one batch's family: the calls in a step, in the order they were made.
THE FIRST NAMED FAMILY WINS, and the rule is deliberately that plain. A step is a run of calls the model made together, and what a person watching calls it is what it OPENED with — "reading three files and then editing one" is a reading step that went on to edit, not a two-icon step. Counting instead would let a batch of four reads and one edit be titled by the reads while a batch of four edits and one read flipped, which is a mark that moves for a reason nobody can see.
ActionWork never wins while anything more specific is present: it is the bucket, so a batch of one unknown service call and one `read` is a reading step. An empty batch, and a batch of nothing but unknowns, is ActionWork.
func ParseActionCategory ¶
func ParseActionCategory(word string) (ActionCategory, bool)
ParseActionCategory reads one word as a family, reporting whether it is one.
IT IS AN EXACT MATCH after case and surrounding punctuation, and there is no synonym table on purpose. A model that answers "running" instead of "run" is not guessed at — it falls to ActionCategoryForTool, which is right about the work by construction. A synonym list would be a second vocabulary that drifts from the one the prompt names, and it would turn a wrong guess into a confident wrong icon rather than into the deterministic one.
func SplitActionLine ¶
func SplitActionLine(raw string) (ActionCategory, string)
SplitActionLine takes the narrator's raw answer apart into the family it named and the sentence it wrote.
THE PROSE SURVIVES EVERY FAILURE AND THE LABEL NEVER DOES. A line with no bar, or a bar with a PHRASE in front of it, is prose that happens to contain the character and comes back whole — so a caption reads exactly as it would have before this format existed, and the mark comes from the tools.
A LABEL ATTEMPT, THOUGH, IS CONSUMED WHATEVER BECOMES OF IT. One bare word before a bar is a model reaching for this format: `investigate | reading the renderer` gives back "reading the renderer" with no family, because leaving the word in would put machinery into the one line on the frame that may not carry any. The rule is narrow — ONE word, no spaces, at most [actionWordMax] characters — because a real five-to-ten-word caption never opens that way.
AND AN ATTEMPT WITH NOTHING AFTER THE BAR IS REFUSED OUTRIGHT: both halves come back empty. `run |` is a model that produced the format and no sentence, and the two alternatives are both worse than nothing — handing back the raw line draws the label and the bar as though they were the work ("run |" on the frame), and handing back the family alone would put a mark beside a caption the narrator never actually replaced. Empty is the caller's signal to keep the deterministic composite already standing in the slot, which is a better line than either.
NOTHING HERE RETRIES. Every outcome is one pass over one answer; the caller has already spent its call and does not ask again.
type AdmissionContext ¶
type AdmissionContext struct {
Version int `json:"version"`
Quotes []AdmissionQuote `json:"quotes,omitempty"`
Evidence []AdmissionHandle `json:"evidence,omitempty"`
}
AdmissionContext is the working context one task is admitted with.
Every field is exported and tagged because this is embedded in the checkpoint record (task_store.go): unexported fields serialise as `{}`, and a resumed task would read an empty document under a full heading.
type AdmissionHandle ¶
type AdmissionHandle struct {
Call string `json:"call"`
Tool string `json:"tool"`
// Input is the OPENING OF the call's arguments, cut at
// [admissionInputLimit] and marked where it was cut. It is enough to
// recognise which call this was; the whole of it is in the record.
Input string `json:"input,omitempty"`
// Outcome is what is known about how the call ended. Unknown is a real state:
// the flag a tool returned does not survive into the transcript, so a context
// compiled after a restart can say a call was answered without being able to
// say whether it succeeded.
Outcome AdmissionOutcome `json:"outcome,omitempty"`
// Detail is a failure's own first line, and only a failure's: knowing that a
// call failed without knowing how is what makes a worker run it again.
Detail string `json:"detail,omitempty"`
// Source is the record the whole result can be read out of — the session
// journal rather than the conversation store, because a store id is not
// something read or grep can open (loop.go states the same rule for a fold
// marker). The call id is the token to grep for.
Source string `json:"source,omitempty"`
// Result is the existing full-result pointer, when available. Unlike a raw
// provider ID in a journal, it names this result even when IDs repeat.
Result string `json:"result,omitempty"`
From string `json:"from,omitempty"`
Depth int `json:"depth,omitempty"`
}
AdmissionHandle is one call that already ran. A successful result's body is deliberately absent: the bytes stay where they are and the worker fetches what it needs.
type AdmissionOutcome ¶
type AdmissionOutcome string
AdmissionOutcome is the four answers there are about a call.
const ( // AdmissionUnknown is the honest default: a result reached the transcript and // nothing in this process recorded whether it was a failure. AdmissionUnknown AdmissionOutcome = "" AdmissionOK AdmissionOutcome = "ok" AdmissionFailed AdmissionOutcome = "failed" // AdmissionUnanswered is a call with no result in the transcript at all — an // interrupted batch, a turn that died — and is rendered as neither success // nor failure. AdmissionUnanswered AdmissionOutcome = "unanswered" )
type AdmissionQuote ¶
type AdmissionQuote struct {
// ID is a deduplication key, never an address. What it identifies differs by
// speaker: the person's turns are numbered as they are heard, so the same
// sentence typed again after a correction is a separate instruction, while an
// assistant line is keyed by its content, because nothing durable numbers
// assistant messages and a position moves under compaction. Two identical
// assistant lines therefore share an id and are carried once.
ID string `json:"id"`
Speaker string `json:"speaker"`
// Text is bounded and may be elided in the middle ([elide]), which is what
// makes Source load-bearing rather than decorative.
Text string `json:"text"`
// Source is the record this was quoted from: the session journal, which a
// worker can open with read and grep. There is no line number — resolving one
// would mean parsing the whole journal at every admission, and a line for a
// sentence that appears twice cannot be told from the other one. The words
// are the grep token.
Source string `json:"source,omitempty"`
// Calls names the tools this text was said alongside. Text and calls are not
// alternatives: a message that reads a result and starts the next call in the
// same breath is the ordinary shape of work.
Calls []string `json:"calls,omitempty"`
// From is whose transcript this came out of: empty for the conversation,
// "task 7" for a node.
From string `json:"from,omitempty"`
// Depth counts the admissions this entry has travelled through. 0 is local;
// past [admissionDepthLimit] it is not carried.
Depth int `json:"depth,omitempty"`
}
AdmissionQuote is one thing somebody said. Speaker and Source are what keep it from reading as a finding: whose sentence it is, and where the whole of it can be read.
type Agent ¶
type Agent struct {
// contains filtered or unexported fields
}
Agent is one conversation. It is safe for concurrent use, but Submit serializes: a second Submit while a turn is in flight queues the message as an injected user message (omp's steering model), so the surface never needs a queue of its own, and hands back its own live channel onto that turn's events — every Submit streams, whether it started the turn or steered it. The methods live in agent.go; the loop they drive lives in loop.go.
func New ¶
New builds one session agent against a live provider. It performs no network request: the client is constructed, the prompt rendered, and the session file — if configured and present — replayed into the transcript.
func NewBeltWorker ¶ added in v0.4.0
func NewBeltWorker(config Config, completer Completer, task *plandb.Task, storePath, rootID string) (*Agent, error)
NewBeltWorker builds one bash-belt worker agent for one store task. The config carries the door's facts about the seat — the model, its key and window, the workspace the run's workers share — and the constructor sets the posture a worker has no parent to inherit: InTask, the belt, the unwatched node's approval floor, and the folder the worker's transcript and spill files land in, which is the task's own record folder beside the store (plandb.TaskDir), where the trajectory the run records lives too.
THE COMPLETER IS THE SEAT'S PROVIDER, carried into the worker through the public door ([Config.completer], read by New). A run hands the conversation's account-aware view ([Agent.beltRunCompleter]) and a test hands a scripted one; nil is the road where nobody handed one and New builds the real client itself.
rootID IS THE RUN THE WORKER BELONGS TO, read off the run's own open handle and never off the file at storePath. The path is where the run's store WAS when the run opened it; the root is which run it is, and a worker's `plandb` refuses a store at that path whose root is another run's (plandb.RunEnv). Empty binds the path alone.
func (*Agent) Abandon ¶
func (a *Agent) Abandon(reason AbandonReason) (Usage, bool)
Abandon lets go of the in-flight turn and reports what it had spent.
It reports false when there is nothing to abandon, which is both the ordinary case and the IDEMPOTENCE the one-line law needs: a turn is abandoned once, so a second call — a duplicated deadline, a surface asking twice — writes no second line and disturbs nothing.
THE SESSION IS FREE WHEN THIS RETURNS. running is cleared, the queues are dropped and the turn's channels are let go of under the same lock, so a Submit arriving on the next keystroke opens an ordinary turn and cannot be refused for a turn nobody is waiting for.
func (*Agent) AnchorWorkspace ¶
AnchorWorkspace changes an owned conversation into a borrowed one rooted at path. It is deliberately unavailable after the first successful anchor: a project-less conversation may acquire its subject, but an ordinary project conversation does not silently become a different project midway through.
func (*Agent) AnswerLaneOffer ¶
AnswerLaneOffer answers the offer standing over this conversation's model, and reports whether there was one to answer.
IT IS THE ONLY DOOR THE SURFACE HAS, and it takes the answer rather than the question: which lane the rescue goes to was decided when the offer was raised, from the frontier that was already computed, because the moment a rescue is wanted is the worst possible moment to start choosing one.
func (*Agent) AnswerSubharness ¶
AnswerSubharness answers the question a running subharness is waiting on, on the run's own node id — the number on its roster row, in its ✕, and the one a person says out loud.
TAKING OVER IS THE THIRD ANSWER and it is not a stop: the run ends where it stands with its journal complete, and what the person typed becomes the run's answer for whoever picks the work up. That is the difference between this and `Cancel("task:9")`, which ends the work and keeps nothing but the trail.
An id nobody is waiting on — a question whose run was stopped, a second press — is ignored rather than reported, exactly as Agent.ResolveConsent ignores a late answer: the answer is simply late, and the surface has already seen the row move.
func (*Agent) ApprovalDial ¶ added in v0.4.0
ApprovalDial reports whether this session can move its own gate at all.
func (*Agent) ApprovalPosture ¶ added in v0.4.0
ApprovalPosture is the posture THIS conversation was set to, "" when nobody has set one. It is the stored word and not the resolved one, for Agent.ConversationEffort's reason.
func (*Agent) AskQuestion ¶
AskQuestion is the door an asker inside this engine puts a question through: the gate, then [Agent.raiseQuestion] with the question's row on the presence desk beside it. It answers the func that takes both back down, and a refusal answers a func that does nothing — a question that did not pass the gate was never raised and has nothing to retire.
THE GATE IS ASKED AGAINST THE RECORD, which is the first rung of the ladder defended in the one place every asker passes through. A question a decision already answers never reaches a person; the asker is handed the decision instead.
func (*Agent) AskRun ¶ added in v0.4.0
func (a *Agent) AskRun(ctx context.Context, rootID, question string, earlier []RunAskExchange) (RunAskAnswer, error)
func (*Agent) Attach ¶
Attach is a reader arriving at a turn IT DID NOT ASK FOR: the whole of what this turn has said so far, then its live tail, on one channel.
It is the door for a surface that keeps several sessions alive and shows one at a time. Such a surface comes back to a conversation it detached from minutes ago and finds a turn half way through an answer; it holds no channel, because the caller that started that turn was a window that is no longer drawing it. Agent.Submit cannot serve it — a steering Submit deliberately starts where it spoke, and it would also put a message in the transcript, which is not what looking at something is.
WHAT COMES BACK IS ONE STREAM WITH NO GAP AND NO REPEAT. The backlog and the subscription are taken under one hold of the hub's lock ([eventHub.attach]), so an event that lands during the call is on exactly one side of the join. Text deltas arrive folded — a paragraph rather than the two hundred words it streamed as — which a surface that appends delta text draws identically.
running is false, and the channel nil, when NO TURN IS IN FLIGHT. That is not an error and not an empty stream: there is nothing to watch, the history is the journal, and a caller told "nothing is running" draws what it already has rather than waiting on a channel that would never carry anything. It is also the answer for a closed session.
stop is how the reader leaves, and it is never nil — a caller may call it without checking running, and calling it twice is calling it once. After it the channel closes, the pump ends and the hub stops holding the subscriber. A SURFACE THAT DETACHES MUST CALL IT: nothing else can tell a reader that has gone from one that is redrawing, and a stream nobody says goodbye to is a parked goroutine and a queue that grows for the rest of the turn.
func (*Agent) AttachReplay ¶
func (a *Agent) AttachReplay() (entries []DisplayEntry, events <-chan Event, stop func())
AttachReplay is Agent.Attach and Agent.Transcript AS ONE READING, for the surface that is about to draw both: the entries to replay, and — when a turn is in flight — that turn's live stream, whose backlog replays the turn's work from its first event.
IT EXISTS BECAUSE THE TWO CALLS MADE SEPARATELY DREW THE RUNNING TURN TWICE. Every completed step of a turn is journaled the moment it completes ([Agent.recordLocked]), so mid-turn the transcript already holds the turn's first half — and the backlog replays that same half again, in its live form, under the copy the replay just drew. Taken under one hold of the lock, the split is exact instead of raced: the entries stop at the turn's floor ([Agent.turnFloor]) and the stream carries the turn whole, so the conversation reads once, with no gap and no repeat.
WHAT STAYS BELOW THE FLOOR IS EVERY WORD A PERSON TYPED. The backlog carries the model's work and nothing else — no event kind re-delivers a user message — so the turn's opening message, and any steer typed into it since, come from the entries or from nowhere. The turn's own asides (a task's landing note drained mid-turn) ride the stream's task events instead and are left out with the rest of the work.
events is nil when no turn is in flight — the entries are the whole record and there is nothing to watch — and stop is never nil, on Agent.Attach's terms: calling it without checking events, or twice, is fine.
func (*Agent) AttachReplayCursor ¶
func (a *Agent) AttachReplayCursor() ([]DisplayEntry, <-chan Event, func(), ReplayCursor)
AttachReplayCursor captures the replay ownership boundary with the same lock as the transcript and current-turn observer. That observer owns the current turn; canonical streams through this cursor are already represented.
func (*Agent) AttachSkills ¶
AttachSkills puts skill names in front of this conversation, in the order given, and returns the set as it now stands. A name already attached keeps its original position rather than moving to the end: the order is the conflict rule the workers read (plan.ComposeSkills — earlier wins), so re-attaching what is already there must not quietly re-rank it.
Blank names are dropped. Nothing here reads the shelf, so an unknown name is kept as written.
func (*Agent) AttachedSkills ¶
AttachedSkills is the set as it stands, in attachment order. The slice is a copy, because the caller is a surface drawing a list while a turn may be running.
func (*Agent) Autonomy ¶
Autonomy returns this project's explicit rows for a surface. The map is a copy: changing a row still goes through SetAutonomy, where the safety floors and atomic write live.
func (*Agent) Cancel ¶
Cancel stops one piece of work and answers with the line to show for it.
The line is the point of the return value: a surface that asked for something to be stopped is owed a sentence saying what that did, in the same way a surface that answered the fuel gate is (Agent.ResolveOrchestrate). An error is only ever an id this session cannot place — an unknown kind, an id that is not a number, or work this session has never heard of.
func (*Agent) CancelWithReason ¶
CancelWithReason is the same door with the words of whoever pulled it.
THERE IS ONE STOP AND THIS IS IT. The person's card asks nothing and carries no words (internal/tui3's stop.go), and the model's `tasks … stop` carries the sentence it was given — but a stop that behaved differently depending on which hand pulled it would be two stops, and the one thing every surface here agrees on is that stopping means the same thing whoever asked for it. So the reason is the ONLY difference: it rides onto the node's record and into the line, and nothing else about the ending changes.
THE REASON REACHES A TASK AND NOTHING ELSE. A run, a sub-harness run and a background job settle with no report of their own for one to be written on, and a reason accepted and dropped would be worse than one never taken.
func (*Agent) ClearAttachedSkills ¶
ClearAttachedSkills takes every name back off and returns how many were on.
func (*Agent) Close ¶
Close ends the session: an in-flight turn is cancelled and waited for, the running nodes of this session's graph are cut and waited for, background jobs are terminated, then the session file is flushed and closed.
The steps are sequential, not concurrent. The turn's wait is first because a running turn is what still owes the journal messages; the graph's nodes and then the jobs come after, because a turn cancelled mid-tool-call may still be the thing that started the work being ended; the file closes last because every one of those can still write to it. Each round EXTENDS the quit rather than racing it: at most one jobShutdownGrace for the graph and one for the jobs, plus one for adaptive runs; each is a grace stragglers share.
The wait is the point. A turn cancelled at Close still has messages to journal — the partial reply it kept, the steering it drained — and closing the file first would drop exactly the tail a resume needs. The wait is bounded because Close is what the person's quit reaches: a turn wedged inside a tool must not hold the process open, and past the grace the journal simply stops accepting writes rather than writing to a closed descriptor.
func (*Agent) Compact ¶
Compact runs a compaction pass now (the surface's /compact). It is a no-op when the transcript is smaller than the keep-recent floor.
func (*Agent) CompactWithFocus ¶
CompactWithFocus is Compact, and THE FOCUS IS NOW IGNORED.
It was one extra instruction for the summarizer — `/compact keep the API decisions and the failing test`, a person saying which part of a lossy summary had to survive. The summarizer was deleted on 2026-08-18 and came back on 2026-09-28 only as a pass's last rung (compact_summary.go), and the surface passes no focus to it: `/compact` takes no argument.
The door stays open with its signature unchanged because the surface calls it (internal/tui3), and a person who types the old form gets the pass they asked for rather than an error about a machine that used to exist.
func (*Agent) ContextTokens ¶
ContextTokens is what the conversation now weighs, in tokens: the same figure the compaction threshold is checked against, under the same lock.
It is THE honest number, and honest means two different things depending on what has happened. When a response has come back, it is the provider's own count of the request it just served — the system prompt, the tool schemas, every tool result, the arguments of every call, all the bytes a surface counting words cannot see. When the transcript has grown since (a 300KB file read that has not been sent yet), the content estimate is larger and wins. See [Agent.meterTokensLocked] for why it is the max of the two.
Zero is a session that has neither sent nor recorded anything, which is the only case where "nothing" is true.
func (*Agent) ContinueRun ¶ added in v0.4.0
ContinueRun picks up a run that nothing is driving, by the number its row wears — the same number every other surface and the stop road already know it by (stoprun.go).
IT REFUSES RATHER THAN REPAIRS, every time, and each refusal names the thing that is in the way in the person's own words:
- a run already going, because a conversation drives one at a time and the honest answer to "carry this on" is that it never stopped;
- a row that is not a run, or no such row at all;
- a run that settled, which has nothing left to carry on;
- a run whose working copy was never written down or is gone from disk ([runCopyTree] holds both sentences).
THE ANSWER IS THE PERSON'S OWN SENTENCE and the error is for the caller. A door that spends money says what it did in the words the person will read about it afterwards.
func (*Agent) ContinueTask ¶
ContinueTask re-arms a settled node so the frontier runs it again. Unknown id, a node that is still going, and a design, a saved-shape run or a quick task are each an error naming which.
THE ASSIGNMENT NEVER CHANGES. words, if any, arrive under [continueAskedLead] as this round's finding, beside the last report. The brief and the acceptance the node was admitted with stay the ones the work is graded against.
func (*Agent) ConversationEffort ¶
ConversationEffort is the rung this conversation was set to, "" when nobody has set one. It is the STORED rung and not the resolved one: a surface drawing the dial has to show what a person chose, and the emptiness law says a scope that chose nothing draws nothing.
func (*Agent) Decisions ¶
func (a *Agent) Decisions() []DecisionRecord
Decisions is this session's record, oldest first, and it is what Question.Check is asked against.
A SESSION WITH NO FOLDER HAS NO RECORD, and answers nothing rather than keeping one in memory: a decision that survives only as long as the process is not a record, it is a cache, and a gate built on one would refuse a question in one window and allow it in the next.
func (*Agent) DefaultEffort ¶
DefaultEffort is the install's own rung, as this session was launched with. It is read-only here: the row lives in the profile and a surface writes it with config.WriteDefaultEffort, because a setting that outlives the process cannot be owned by a session.
func (*Agent) Delegates ¶
func (a *Agent) Delegates() DelegateReport
Delegates is the report for this conversation. A build that carries none answers the zero report, and the surface draws no rows.
func (*Agent) DetachSkill ¶
DetachSkill takes one name back off, and says whether it was there. Taking off a name nobody attached is not an error: a surface offering a list of checkboxes has no way to know the list changed under it.
func (*Agent) EarlierHistory ¶
func (a *Agent) EarlierHistory() EarlierHistory
EarlierHistory is what a surface needs to scroll back through a compaction: the conversation above the latest pass, and the floor beneath which the live transcript is that same conversation rewritten.
IT IS HISTORY AND NOT CONTEXT. Nothing here sends it, the model does not carry it, and it does not change when the person rewinds — a rewind edits the live transcript, and the region above the marker was already out of the model's hands before the cut was offered.
IT IS EMPTY IN THREE CASES, and a surface must behave exactly as it always did in all three: a session that was never compacted, a session compacted by a build that did not write the window's length ([compactionOverlap] explains why that cannot be guessed at), and a session with no journal at all. The third is the honest one to remember — a memory-only conversation has no file to have kept the words the pass took out.
WHAT IT COVERS when it is not empty: the transcript exactly as it stood one instant before the latest pass edited it. For a session compacted once that is the whole conversation from its first word, in the original lines, before anything was stubbed or folded. For a session compacted more than once it still opens on the conversation's first words — a pass never folds what a person said — and carries the older passes' own edits in the middle of it, because those edited lines are what the file holds there. See [replayedSession.earlier] for why the older markers are applied rather than walked through.
func (*Agent) Elsewhere ¶
Elsewhere is ReadElsewhere over this session's own project bucket, with this session left out of it.
It answers the empty reading for a session with no folder — a memory-only conversation has no bucket to look in and no id to leave out, which is Agent.ProjectPresence's own answer to the same shortage.
func (*Agent) ElsewhereExcept ¶
ElsewhereExcept is the same reading with MORE OF THE CALLER LEFT OUT: this session, and every other session id the caller says is its own.
A surface holding several conversations at once passes the ids of the ones it is not drawing (internal/tui3's keeper.go). They are live windows on this machine and every OTHER terminal sees them as exactly that, correctly — but they are not elsewhere from here, and a row telling somebody to go to a window they are already inside is the same wrong refusal home used to make about another project.
func (*Agent) FollowUp ¶
FollowUp queues a message to be asked AFTER the current turn ends, and hands back the channel that turn will stream on.
It is the second queue beside steering, and the difference between them is what each drain does. Steering lands INSIDE the running turn at its next step boundary: the model reads it as one more thing the person said mid-work. A follow-up waits for the work to finish and then starts a turn of its own — the same machinery, the same events, a normal new turn as far as any surface is concerned. That is the right shape for "and after that, do this", which as steering would arrive as an interruption of the thing it is meant to follow.
ONE AT A TIME is the whole steering mode here: a turn's end dequeues exactly one message, and the rest wait for the end of the turn that one starts. A drain that started several turns at once, or spliced the whole queue into one message, would be a decision the person did not make.
A follow-up queued while nothing is running starts immediately: there is no turn end coming to drain it, and a message that sat in a queue until the person happened to ask something else would be a silent hold.
func (*Agent) Forget ¶
Forget drops the best match for a query and answers with the title it dropped, or "" when nothing matched.
func (*Agent) HandUnverifiedToModel ¶
HandUnverifiedToModel gives ONE node's decision to the model instead of taking it: the surface's "decide these yourself from now on", pressed on the card that is asking right now (internal/tui3's taskdone.go).
IT DOES NOT RESOLVE ANYTHING. The node stays exactly as it is — unverified, branch kept, dependents waiting — and what changes is who is holding the question: a line lands on the steering queue, the session wakes if it is idle, and the model reads the work and calls `tasks … resolve` itself. That is the same path a landing under `task.settle = auto` takes, said about a node that already landed, so the two doors cannot disagree about what the model is being asked to do.
The error is the one Agent.ResolveUnverified gives for the same node, because a surface pressing this on work that somebody else has already decided needs the same sentence either way.
func (*Agent) HarnessDesigns ¶
HarnessDesigns is the standing subscription to what this session's harness designer is doing: the design starting, the card asking whether to keep what it wrote, and the notes that say a design failed, was declined or was saved.
It exists for Agent.TaskUpdates's reason. A design outlives the turn that asked for it — that is the point of it — so its most important event has no Submit channel to arrive on. A surface that draws harness cards subscribes once at startup; a surface that does not never calls this and pays nothing.
The channel is never closed by a turn ending, and a surface holds it for the life of the session.
func (*Agent) HoldQuestion ¶
func (a *Agent) HoldQuestion(kind QuestionKind, token string)
HoldQuestion stops the clock on one question a person is looking at, without answering it, and tells every window so.
IT IS Agent.ResolveQuestion'S SHAPE FOR THE OTHER THING A KEY CAN MEAN. A key pressed on a question is evidence that somebody is there, and a clock that answered over their hands would be the one answer this program must never give on their behalf (tui3's [app.tickQuestion] states it for the surface's own reading clock). The lane's token is the token an answer names, so a surface holds a question with what it already has.
THERE IS NO WAY BACK, for that same reading: a clock that resumed after a few idle seconds would fire exactly when somebody had looked away mid-decision.
func (*Agent) HoldTask ¶
HoldTask removes the admission clock from one pending proposal without answering it. The updated proposal is broadcast through the turn's ordinary event lane so every watching surface clears the same deadline.
func (*Agent) Interrupt ¶
func (a *Agent) Interrupt()
Interrupt cancels the in-flight turn, if any. The partial reply is kept in the transcript; the turn's stream ends with EventTurnDone. A tool call blocked on a consent request (consent.go) is released by the same cancellation and refuses, so the turn ends rather than waiting on a question nobody is going to answer now.
It empties BOTH queues, and they empty differently because their drains do different things. The follow-up queue is DROPPED here: draining it would start a new turn, and a stop that was followed by the session working again is not a stop. The steering queue keeps its own law — the turn's end drains it into the transcript (the person typed it, so it is part of the record) — and that drain starts nothing, so it cannot resurrect anything. Both queues are empty once the interrupted turn has finished.
func (*Agent) InterruptFor ¶
InterruptFor is the same door for machinery that is not a person: a window taking the conversation over, a tab closing, a hosted session being retired.
IT EXISTS SO THAT THE TURN CAN ACCOUNT FOR ITSELF AFTERWARDS. Everything below is identical either way — the same queues are dropped, the same work is cut — and the only difference is the word the cancelled context carries, which is what decides whether the person is owed a sentence about a reply that never arrived (stopcause.go).
func (*Agent) InterruptNamed ¶
InterruptNamed is the same stop with the window the door believed had gone, when the door has one to name. The name is a label and nothing else — never an identity — and empty is a stop that does not know.
func (*Agent) Land ¶
func (a *Agent) Land(folder string) (FolderLanding, error)
Land brings one folder's work home, through the roads the tasks already use: a branch is committed and merged ([taskTree.comeHome]), a copy is laid back over the folder by name ([taskTree.landMirror]). There is no third landing and no copier of its own here — a second one would be a second answer to "what does it mean for work to arrive".
IT LANDS THE FOLDER WHOLE. Picking files out of a landing is a refinement this does not have, and the manual says so rather than letting somebody find out by looking for the key.
AND THE COPY GOES WITH IT, whatever the outcome: a merged branch has nothing left to hold, and a conflicted one keeps its branch — which is where the work actually is — while the record here is dropped, because a copy this conversation would go on writing into after its branch was kept would be work piling up behind a landing that already failed.
func (*Agent) LandingFor ¶
func (a *Agent) LandingFor(folder string) (FolderLanding, bool)
LandingFor is what a landing WOULD do — the folder, its name and every file waiting — without doing any of it. It is what the card shows before the person says yes.
func (*Agent) Memories ¶
func (a *Agent) Memories(query string) ([]MemoryLine, error)
Memories lists what is kept, newest-touched first — or the best matches for a query, when one is given.
func (*Agent) NameTeam ¶
NameTeam asks the naming role for a short name for a group of conversations, given their titles. It makes one call, which the caller bounds with ctx, and returns the name or an error; an answer that is not a name is an error, never a name.
func (*Agent) NeedsPerson ¶
NeedsPerson reports whether this conversation is stopped on a question only a person can answer — an approval, a connect offer, a sub-harness offer, a task proposal, a standing card, an intake card chat raised for a saved program, an adaptive run waiting at its fuel gate, or a landed task waiting on somebody's word about whether its work holds.
IT IS THE PRESENCE FILE'S OWN TEST, ASKED DIRECTLY. A surface in this process must never answer it by reading its own presence file back: that file is written on a five-second heartbeat and believed for fifteen ([presenceHeartbeat]), so a count built on it would lag a question the person is looking at, and would be this process reading its own writes off a disk.
func (*Agent) NewsKey ¶
NewsKey is [Agent.newsKey] for the transport that has to file a connection under it (internal/remote's news.go). It is the only reason the identity is exported, and it is deliberately not on any interface: an engine door built around something other than this agent simply has no key, and a host that cannot name a conversation fans nothing out to it rather than fanning everything out to everybody.
func (*Agent) NoteConnected ¶
NoteConnected is the other door: an account connected from the SURFACE, with no tool call waiting on it — /connect while the conversation sits idle.
It does the two things the tool path does after a successful attempt, and neither of them is a turn: the family goes on the belt, and one line goes onto the AMBIENT queue (agent.go's [Agent.enqueueAmbientNote]), so the model reads it whenever the person next says something. A connected account is not news anybody is standing there waiting for an answer about — the person is looking at the surface that just told them — so it must not start a paid turn.
A service this build does not know, or a session with the feature absent, does nothing at all rather than queueing a line about a thing that cannot be used.
func (*Agent) OpenQuestions ¶
OpenQuestions is every decision this session is waiting on somebody for, oldest first, each as one Question.
IT IS DERIVED AND THERE IS NO SECOND STORE (the file header, and pending.go's own law one layer down). This walks the WAITS the lanes already keep — the consent map, the connect asks, the harness asks, the standing answers, the task proposals, the sub-harness offers and questions, the orchestrator's pause and the graph's unverified nodes — and asks [Agent.questionWords] only what each of them SAID. A list kept beside those would be a second place a question could be open, and the two would disagree the first hour a lane learned to close one on a road that forgot to tell this file.
IT IS WHERE `NEEDS SOMEBODY` IS COUNTED FROM, and that is the audit's fourth finding closed: a landed task's `your call` lived in a registry of its own that [Agent.waitingOnPerson] never folded in, so a task sitting on somebody's decision left home, the switcher and the tab signal all saying there was nothing to do.
THE LOCKS ARE TAKEN ONE AT A TIME AND NEVER NESTED, which is taskpresence.go's standing rule about anything holding a lock of its own: the question lanes are the agent's, the graph is its own, and a run's pause is the orchestrator's. Holding a.mu across another lock is holding the lock Interrupt has to be able to take.
func (*Agent) OrchestrateNodeJournal ¶
OrchestrateNodeJournal is the transcript door behind one published run node. The registry supplies the session identity and the snapshot supplies the node: neither a guessed path nor a file left by another run is evidence that this session knows the work. A machine with no home keeps the node in memory, on the same terms [orchestrateJournalPath] uses when it creates the worker.
func (*Agent) OrchestrateSnapshot ¶
func (a *Agent) OrchestrateSnapshot(id string) (orchestrate.Snapshot, bool)
OrchestrateSnapshot is the room's poll: the run's latest published shape, or false when the id names no run this session knows.
func (*Agent) Orchestrations ¶
Orchestrations is the standing subscription to every adaptive run this session is driving: the planner's notes, the fuel gauge crossing its warning mark, and the gate.
It exists for Agent.TaskUpdates's reason and answers to the same law: a run outlives the turn that asked for it, so its most important event — the gate — has no hub to arrive on. A surface that draws runs subscribes once at startup; a surface that does not never calls this and pays nothing.
func (*Agent) OtherProjects ¶
func (a *Agent) OtherProjects(now time.Time) []OtherProject
OtherProjects is ReadOtherProjects over the root this session's own folder sits in, with this session's project left out.
THE ROOT COMES OUT OF THE PLACE AND NOT OUT OF PlacesRoot. The session folder already knows where it lives — bucket, then root, two elements up — and reading the state root a second way would be a second answer to go wrong the day one of them is pointed somewhere else. It is also what lets a test build a machine in a temporary directory.
It answers nil for a conversation with no folder, which has no root to look in — Agent.ElsewhereExcept's own answer to the same shortage.
func (*Agent) PendingConnect ¶
PendingConnect lists the connect questions still waiting for an answer, oldest first. It is Agent.PendingConsent for the other question: a surface redrawing itself mid-turn — a resize, a reattach — needs to know a question is outstanding without having kept the event.
func (*Agent) PendingConsent ¶
PendingConsent lists the requests still waiting for an answer, oldest id first. It exists for a surface redrawing itself mid-turn — a resized window, a reattached view — which needs to know a question is outstanding without having kept the event.
func (*Agent) PendingDecisions ¶
func (a *Agent) PendingDecisions() []PendingDecision
PendingDecisions is every node in this session that finished with nobody able to say whether its work holds, in admission order.
IT IS THE ONE LIST, and it is exported because three kinds of caller need it: a surface drawing the answers row, the model's own `tasks` tool, and the tests that hold this package to the law above. Resolving a node — accept, look again, not right, or a late verdict landing — takes it off this list by moving its state, and nothing else has to remember to.
func (*Agent) PendingSubharnessAsk ¶
PendingSubharnessAsk reports whether a run is waiting on a person right now, and is what a surface asks before drawing an answer box for a room it has just walked into. Zero is nothing waiting.
func (*Agent) PendingTasks ¶
PendingTasks lists the proposals still waiting for an answer, oldest id first. It is Agent.PendingConsent for the other question: a surface redrawing itself mid-turn — a resize, a reattach — needs to know a proposal is outstanding without having kept the event.
func (*Agent) Places ¶
Places is the set this conversation is about, newest first. A surface draws it; nothing here changes when it is read.
func (*Agent) PlanAmend ¶ added in v0.4.0
PlanAmend prepends text to a task's description, the way the CLI's `task amend --prepend` does: the plan learns while it runs, and the text lands at the front of the work order the next worker reads.
func (*Agent) PlanCancel ¶ added in v0.4.0
PlanCancel ends a task, its descendants and the work hard-depending on it. The cascade is the store's own law.
THE CANCEL CARRIES THE STOP'S OWN WORD, because a person pressed it. The store keeps the reason with the ending and a row reads `stopped` only off that word ([planTaskStopped]); this cancel used to carry none, so a part a person stopped — and everything its cascade took down — read `incomplete`, the word for work that ran and came up short on its own.
THE RUN'S OWN TASK IS STOPPED AS THE RUN. The store refuses every verb on it, because no worker may end the run it is part of; a person may, and the stop they are owed is the run's (stoprun.go), never the store's sentence about who owns what.
func (*Agent) PlanNote ¶ added in v0.4.0
PlanNote leaves a note in the person's own voice on one task. It is the soft steering beside the hard verbs: the task's worker is handed it between its own steps, on the road internal/run's note channel carries, and the note carries the person as its author the way the store spells that (AddPersonNote), so a surface draws the two voices apart.
func (*Agent) PlanNoteFromChat ¶
PlanNoteFromChat leaves a note on one task in THE CONVERSATION'S voice, and it is Agent.PlanNote with the one difference that matters: the author.
THE MODEL IS NOT THE PERSON, AND THE WORKER MUST BE ABLE TO TELL. A note arriving named as the person is a note a worker may read as authority, and a conversation that could write in the person's voice could grant itself permissions nobody gave it — the same hazard, and the same answer, as the `say` door's ([Agent.relayToTask]). So the note carries plandb.NoteAgentChat and is a worker-side note by the store's own column, and every reader draws it apart from the person's own.
func (*Agent) PlanPause ¶ added in v0.4.0
PlanPause holds a task and everything under it out of the ready frontier without changing its rung: running steps finish, and nothing new in the subtree is launched until it is resumed.
func (*Agent) PlanPriority ¶ added in v0.4.0
PlanPriority sets a task's priority through the store's revision verb, the one road the store has for it. The revision is a contract change, so the store refuses a task that has already started.
func (*Agent) PlanResume ¶ added in v0.4.0
PlanResume releases the hold PlanPause set. A task that was not held is left alone and answers no error, so a surface may resume without asking first.
func (*Agent) PlanRunSummary ¶ added in v0.4.0
func (a *Agent) PlanRunSummary(rootID string) (RunPlanSummary, bool)
PlanRunSummary reads the last stored summary and says whether the store's current task-and-question shape has moved since it was written.
func (*Agent) PlanSpend ¶ added in v0.4.0
func (a *Agent) PlanSpend(since time.Time) []PlanSpendLine
PlanSpend answers THIS conversation's plan spending rolled up by seat: one line per role the store gave work, carrying the model that seat spent most through over the dollars and calls it wrote down since a moment.
NIL IS THE HONEST ANSWER for a conversation with no plan store and for a store with nothing priced in the window, exactly as Agent.PlanTasks answers nil for a conversation with no plan: the spend page draws its block's heading and whisper from that emptiness rather than a zero line, which is the emptiness law applied to money.
THE ROLLUP IS SUMMED HERE AND NOT BY THE STORE. plandb.Store.SpendBy groups by seat but names no model beside it, and the page draws the model on the seat's own line, so the ledger is read the way [planSpendByTask] reads it — a read-only connection beside the writer, so a store that will not open as a reader answers nothing rather than failing the read.
func (*Agent) PlanTaskPage ¶ added in v0.4.0
func (a *Agent) PlanTaskPage(id string) (PlanTaskPage, bool)
PlanTaskPage answers one task's page — its row, its description, its notes and its steps — for the id a surface was handed. False is the answer for a task this chat did not spawn, whether it is another conversation's or no task at all: the page is the chat's own reading of its own plan.
func (*Agent) PlanTaskWork ¶
func (a *Agent) PlanTaskWork(id string) (PlanTaskWork, bool)
PlanTaskWork answers the working copy of the run one store task belongs to. False is a task this conversation's plan does not hold.
func (*Agent) PlanTasks ¶ added in v0.4.0
func (a *Agent) PlanTasks() []PlanTaskRow
PlanTasks answers the plan this conversation seeded, as rows ready to draw, in the store's own admission order. Nil is the honest answer for a conversation with no plan — the experiment's switch is off, or no store was ever seeded — and an empty slice (not nil) is a plan that holds only other chats' work: the store is there and this chat's part of it is not.
func (*Agent) ProjectPresence ¶
func (a *Agent) ProjectPresence() []SessionPresence
ProjectPresence is the OTHER live windows open on this session's project — this agent's own bucket, with this agent left out of it.
It answers nil for a session with no folder, which has no bucket to look in and no id to leave out.
func (*Agent) PromoteCall ¶
PromoteCall sends a running foreground bash call to the background and answers with the line that names the job it became.
It is the SURFACE's door onto exactly the machinery the timeout uses — one seam, one adoption, one kind of job — because a key that killed and restarted the command would be the throw-away this whole file exists to remove. It answers false when there is no such call running, when the call has already finished, and when the turn has been interrupted; internal/tui3 draws no key in the first case, which is a capability that cannot work being absent rather than broken.
func (*Agent) ProposeTeams ¶
func (a *Agent) ProposeTeams(ctx context.Context, in TeamProposalInput) (TeamProposal, error)
ProposeTeams asks the naming role, once, which teams the conversations in in could form and which existing teams more of them belong in. The caller bounds it with ctx. An answer that is not the JSON asked for is an error, never an empty proposal; an empty proposal is an answer.
func (*Agent) Reasoning ¶
Reasoning is the level the model now in use will be asked for, "" when none is set.
func (*Agent) ReasoningFor ¶
ReasoningFor is the level held for one model id, whichever model is in use. It is what a picker asks while drawing a row for a model nobody has switched to yet.
func (*Agent) ReasoningLevels ¶
ReasoningLevels is every model id somebody has dialled and the level it holds, as a plain map a wire can carry.
IT HANDS BACK A COPY. The agent's own map is written under its lock by every picker keystroke, and a caller ranging over the live one while somebody presses ctrl+t is a race — so the copy is the contract, not an optimization. Nothing is here for a model set back to off: absence is stored as absence.
func (*Agent) RebuildApprovalGate ¶ added in v0.4.0
RebuildApprovalGate builds the gate again for the posture in force and pushes it — what a consent card's banked rule and a dropped one call after they have changed the rows underneath (cmd/codeaf's chatv3_approval.go).
THE POSTURE IN FORCE IS THE RESOLVED ONE, so a rule banked inside a conversation that was walked to `allow` lands on an `allow` gate and not on the rows' own, and a rule banked under `--yolo` keeps the flag's open gate exactly as it did before this scope existed.
func (*Agent) RedoStronger ¶
RedoStronger runs a task again with a stronger crew: every seat nobody pinned steps up one (crewroute.Decide with Stronger set), and the router's log is told the first crew under-served this class here, so the next task of the class in this repository starts a step higher until enough accepted work decays it back. row 0 means the newest task this conversation started.
IT IS ONE TASK'S ASK AND NOTHING STICKS: the pins and the allowed rule are the panel's, untouched. A crew already at the top answers crewroute.ErrStrongest in words, and nothing starts.
func (*Agent) ReferPlace ¶
func (a *Agent) ReferPlace(path string, arrival PlaceArrival) (PlaceRef, error)
ReferPlace is THE DOOR A SURFACE CALLS when a person names a folder — the picker, a directory dropped on the window, `/attach` on one, a path they typed. There is exactly one so that every road in leaves the same record.
It validates against the disk rather than trusting the caller: a place is a directory that IS THERE, because the whole value of the set is that the ladder can hand a ground to work without stopping to wonder. A relative path or a `~` is read against the conversation's own workspace, which is where the person is standing when they say it.
THE PATH IS SNAPPED TO THE REPOSITORY ROOT when it sits inside one, for [groundRoot]'s reason: a branch is cut from a repository and not from a directory inside it. Chose preserves each independently selected context scope: two subdirectories may share a working ground without becoming one attachment.
func (*Agent) RefreshRunSummary ¶ added in v0.4.0
func (a *Agent) RefreshRunSummary(ctx context.Context, rootID string, lastLook time.Time) (RunPlanSummary, bool)
RefreshRunSummary pays for one worker-tier call only when the run moved. Any unavailable or malformed answer preserves the last good reading.
func (*Agent) Remember ¶
Remember writes one thing down now and answers with the title it landed under. It is /remember and it is the `remember` tool, which are the same errand asked by two different mouths.
It goes through the same decide-and-apply path the post-turn pass does, so telling codeaf twice in two sessions that you prefer tabs refines one memory instead of making two.
func (*Agent) RememberScoped ¶
RememberScoped is Remember with the blast radius named: something true about you everywhere, only inside this project, or only on this machine.
func (*Agent) Remembers ¶
Remembers reports whether this session has a brain at all.
It is exported for the surface's sake and it is the difference between two very different lines on screen: "nothing is remembered yet", which is a fact about an empty store, and "memory is off for this session", which is a fact about the wiring and names the row that changes it. A surface that could only see the error would say the first when it meant the second.
func (*Agent) RemovePlace ¶
RemovePlace takes one folder back off the conversation: off the set, off meta.json, and out of what the next request tells the model (placescontext.go).
IT IS THE OTHER HALF OF Agent.ReferPlace AND THE SURFACE'S ONE DOOR OUT. A folder indicator a person can see and cannot dismiss is a mistake they have to open a new conversation to correct, and a conversation still carrying a folder somebody removed from their screen would be the surface and the model disagreeing about what this is about.
IT WORKS ON A FOLDER THAT IS NO LONGER THERE. The set is a history and keeps a record whose directory has been deleted (see [loadPlaces]), so a remove that insisted on stat'ing first would leave exactly those records unremovable — the path is read the ordinary way when the disk can answer, and taken as written when it cannot.
A FOLDER THIS CONVERSATION IS NOT ABOUT IS REFUSED RATHER THAN IGNORED, which is Agent.SetPlaceMode's reading: a caller told "done" about a path that was never on the set has been told something false about which folders are attached.
func (*Agent) ReplaceQuestion ¶ added in v0.4.0
ReplaceQuestion stops the pending turn before submitting the revised request. No answer is recorded and no permission is granted by this operation.
func (*Agent) ResolveConflict ¶
ResolveConflict spends ONE MORE merge round on a node whose branch would not fasten, on the person's word — the `[a] resolve it` of the card.
AND ON A NODE WHOSE GROUND MOVED, which reaches the same card by the other road (task_run.go's [Agent.landShifted]). The round is exactly the right verb there and it needs nothing added: the person's branch is merged into the task's branch — often with nothing at all to resolve, since the branch would have fastened — the check runs again over the two changes together, and the landing is retried. Nothing here asks whether a marker was ever written, so the shift road was never refused; what it lacked was a card that offered it.
IT RETURNS BEFORE THE ROUND DOES, for [Agent.reauditTask]'s reason: a round buys a model call, and a keypress that blocked on one would be a wedged surface. The node stays exactly where it is — needing a look — until the round lands, and when it does the person and the model hear about it on the same lane every other landing rides.
IT REFUSES IN ONE LINE, which is the whole of what a surface can draw. There are two refusals and they are different facts: a node with no working copy left has nothing to resolve IN, and a node whose round is already running must not be given a second worker in the same directory as the first.
func (*Agent) ResolveConnect ¶
ResolveConnect answers one EventConnectAsk. A surface hands back the id the event carried and what the person said.
A YES TO A QUESTION THAT WANTED A TYPED ANSWER IS NOT AN ANSWER. The account needs a key, or the one thing its address is missing, so there is nothing a bare yes could start; it is read as a decline rather than as a connection that then fails for a reason nobody said out loud. A surface that means yes to one of those sends the answer through Agent.ResolveConnectKey.
An id nobody is waiting on — a question the clock already answered, a second click, a turn that was interrupted — is IGNORED rather than reported, exactly as Agent.ResolveConsent ignores a late answer: the answer is simply late, and the surface has already seen the attempt end.
func (*Agent) ResolveConnectKey ¶
ResolveConnectKey answers one EventConnectAsk that carried NeedsKey with the key, or the one thing the service's address is missing, that the person gave.
AN EMPTY ANSWER IS A DECLINE. A surface whose question was dismissed, or whose field was left blank, has one thing to send and no separate word for "not now" — and a blank answer would be refused by the service anyway, an ugly sentence later for a plain no now.
A typed answer sent for a question that wanted an ordinary browser trip is ignored: there is nothing to do with it, and connecting on the strength of it would connect an account by a route nobody asked about.
func (*Agent) ResolveConsent ¶
ResolveConsent answers one EventConsentRequest. An id nobody is waiting on — a question whose turn was interrupted, a double click — is ignored rather than reported: the answer is simply late, and the surface has already seen the turn end.
func (*Agent) ResolveConsentRemember ¶
func (a *Agent) ResolveConsentRemember(id uint64, allow bool, scope ConsentScope)
ResolveConsentRemember answers one request and says how long the answer lasts. An unknown scope is read as ConsentOnce: the narrow reading is the safe one, and a typo must not silently widen an approval.
The memo is written BEFORE the answer is delivered, and it is written even when the call that asked has already given up. The scope is a standing instruction about the tool ("stop asking me about read"), not a property of the call that happened to prompt it, so an interrupt racing the click must not quietly turn "always" into "once".
func (*Agent) ResolveHarness ¶
ResolveHarness answers one EventHarnessOffer: true runs the harness, false is the ordinary turn. An id nobody is waiting on — an offer whose turn was interrupted, a second click — is dropped rather than reported, exactly as a late consent answer is.
The model is WHICH MODEL THE ANSWER WAS GIVEN ABOUT, and EMPTY IS THE ONE THE OFFER CARRIED — which is every surface that draws the card as it arrived and hands the answer straight back. A surface that shows the model on the card (tui3's harness.go) returns what it showed, so what runs is what the person read; a word this session cannot resolve falls back to the offer's own model rather than starting a run on a model nobody has. It is resolved through the same matcher the offer's own word went through, so the two cannot disagree about what "opus" means.
func (*Agent) ResolveOrchestrate ¶
ResolveOrchestrate answers one EventOrchestratePause: "topup:<dollars>" resumes with a raised cap, "finish" jumps to synthesis over partial results, "stop" settles the run with its partial trace.
What comes back is the line to show for it — the gate is a question, and a surface that answered one is owed a sentence saying what that answer did.
func (*Agent) ResolveQuestion ¶
ResolveQuestion is THE ONE DOOR every answer in this engine goes through.
It is how answers.go's first law — AN ANSWER IS APPLIED THROUGH THE SAME RESOLVER A SURFACE USES — stays literally true across eleven lanes instead of the three it covered. This function does not decide anything: it reads which lane the answer names and hands it to that lane's own resolver, which is exactly what the card in a window calls, what home's chip row reaches through answers.jsonl, and what the `--host` link replays. There is no second place that knows what "yes" does.
A LATE ANSWER IS IGNORED AND NOTHING SAYS SO (answers.go's second law). Every resolver below already drops an id nobody is waiting on, so this adds no staleness rule of its own; it hands the id over and lets the one rule that exists apply. That is why an answer to a question that has already been answered comes back nil rather than as an error — it is not a failure, it is a second click.
AND IT REACHES THE THREE LANES NOTHING COULD REACH. The audit found Agent.ResolveConflict, Agent.TakeBackDecision and Agent.AnswerSubharness with a door apiece and no caller anywhere in the product: work that stopped on a question no surface in this program could draw, let alone answer. They are on this door now, so a surface has one thing to call.
func (*Agent) ResolveStanding ¶
func (a *Agent) ResolveStanding(id uint64, answer StandingAnswer)
ResolveStanding answers one EventStandingProposal. An id nobody is waiting on — a card the clock already declined, a second click, an interrupted turn — is ignored, exactly as Agent.ResolveTask ignores a late answer.
func (*Agent) ResolveSubharness ¶
func (a *Agent) ResolveSubharness(id uint64, run bool, input json.RawMessage)
ResolveSubharness answers one EventSubharnessProposal: whether to run the program, and the form as the person left it.
TRUE RUNS IT AND NOTHING ELSE DOES. There is no other path from a proposal to a launch — no clock that approves, no default, no "they did not object" — which is what makes "never silent auto-execution" a fact about this code rather than a promise about it.
The input may be nil, and nil means "as it was raised": a surface that draws the card read-only and offers one key is answering the same question as one that lets every field be edited, and neither has to know how the other spells the form.
An id nobody is waiting on — a card whose turn was interrupted, a second press — is ignored rather than reported, exactly as Agent.ResolveConsent ignores a late answer.
func (*Agent) ResolveTask ¶
func (a *Agent) ResolveTask(id uint64, answer TaskAnswer)
ResolveTask answers one EventTaskProposal. A surface hands back the id the event carried and what the person said about it.
An id nobody is waiting on — a proposal the clock already approved, a second click, a turn that was interrupted — is IGNORED rather than reported, exactly as Agent.ResolveConsent ignores a late answer. The answer is simply late, and the surface has already seen the node start or the turn end.
func (*Agent) ResolveUnverified ¶
func (a *Agent) ResolveUnverified(id uint64, resolution TaskResolution, why string) error
ResolveUnverified is the person's answer to a node no auditor could judge.
It is NOT Agent.ResolveTask, which answers a PROPOSAL — should this work start — and the two are spelled apart on purpose: one is a decision about work that has not happened, this is a decision about work that has.
An unverified node is the one state in this graph that WAITS ON A HUMAN. It is not stuck by accident and it is not going to resolve itself: the auditor was asked twice and said nothing both times, so the only remaining source of a verdict is somebody who can read the diff. Until they do, the branch sits where it was kept and the dependents sit queued — which is the honest position, because an unverified claim is not evidence, and failing them on the strength of an audit that never happened is the exact defect this whole path exists to remove.
THE THREE ANSWERS GO THROUGH THE GATE'S OWN SETTLE. Accepting merges the branch with [taskTree.comeHome], the same call a VERIFIED verdict makes; refuting fails the node and lets the frontier cascade; re-auditing runs the audit again and lands whatever it says. Nothing here is a second way to finish a node — it is the same finish, reached by a different judge.
It is exported because two callers need it: a surface with a person in front of it, and the model through the `tasks` tool (tools_tasks.go).
func (*Agent) ResolvedApprovalPosture ¶ added in v0.4.0
ResolvedApprovalPosture is the posture the gate is actually standing at, whichever scope decided it: the conversation's own word, else the launch's (`--yolo`), else the settings rows as they stand. It is what a surface draws.
`auto` RESOLVES THROUGH TO THE ROWS and never reads as a word of its own: a conversation handed back to the rows is running at whatever they say, and that is the fact a person standing at the chip needs.
func (*Agent) ResolvedEffort ¶
ResolvedEffort is the rung this session's next turn will actually ask for, whichever scope decided it. It is what a surface shows when it wants to say what is HAPPENING rather than what was chosen.
func (*Agent) ResumeStoppedTurn ¶
ResumeStoppedTurn asks the person's last question again, once, when this conversation was opened onto the exact shape resume.go describes, and reports whether it did. It answers nil and false in every other case, which is nearly every conversation ever opened.
THE FACT IS SPENT WHEN IT IS READ, whether or not a turn starts. A window that takes a conversation may attach to it more than once — the switcher brings it forward, the keeper hands it back — and a question asked twice is two answers and two bills for one thing the person typed. It is cleared here, under the lock, before anything else can decide anything.
THE MESSAGE IS THE ONE ALREADY IN THE TRANSCRIPT. Nothing is appended: the person's words are in a.messages because the journal put them there, and in the file because the stopped turn wrote them. A second copy would be a conversation in which somebody said the same thing twice ([userMessage.resumed] is that mark).
AND IT IS THE ORDINARY TURN DOOR. [Agent.startTurnLocked] is what a typed message reaches, so steering, presence, the title, the checkpoint and the retry ladder all behave here exactly as they behave everywhere else — and the stopped attempt's reasoning and half-written reply are simply gone, because nothing kept them.
func (*Agent) RetargetTask ¶
func (a *Agent) RetargetTask(id uint64, model string) (ModelLanding, error)
RetargetTask chooses one task's model for its next turn or continuation. Unknown ids, unsupported task kinds and unknown models are refused.
IT IS THE SANCTIONED EXCEPTION TO THE FREEZE, and the header of this file says why in full: the id is settled at admission so that a `/model` in the conversation cannot move work nobody chose it for, and a person standing in this node's room choosing a model for THIS node is not that. The conversation stays on its own model, every other node stays on its own, and a task admitted after this one still takes the ordinary ladder — `task.model` from settings, else the conversation's ([Agent.defaultTaskModel]).
Settled ordinary tasks save a separate continuation choice. Their historical model and state stay unchanged until ContinueTask starts the next attempt.
SO DOES A RUNNING NODE WHOSE WORK IS BEING CHECKED, for the reason spelled out at the read below: the worker's reading is over, there is no turn left for the pick to reach, and moving the frozen id would make the row name a model that never ran the work.
THE WORD RESOLVES THROUGH ADMISSION'S OWN LADDER (taskmodel.go), so a room and a proposal cannot disagree about what "opus 5" means. A word that fits more than one model is a QUESTION and this door has nobody to ask — the shortlist is a thing a proposal card carries, and there is no card here — so it comes back as the same refusal a too-vague proposal gets, naming the candidates. The surface's own door never raises it: its picker offers concrete catalog ids, so every word that reaches here from a room is already exactly one model.
THE SWITCH LANDS AT THE NEXT REQUEST, and that is Agent.SetModel's own door rather than a second mechanism: a step whose request has produced nothing the person could use lets go of it and asks again on the new model at once, and a step whose answer is already arriving finishes that answer and carries the new model into everything it asks for after (steer.go's THE PERSON'S WORD WINS). The answer says which of the two happened, so a surface can tell them.
It is never the next TURN. A task step is one turn, and the measured failure this door was changed for was a step thirteen minutes into a wait the person could do nothing about.
func (*Agent) RetryTask ¶ added in v0.4.0
RetryTask restarts incomplete work without changing its identity or assignment.
func (*Agent) Rewind ¶
func (a *Agent) Rewind() ([]DisplayEntry, error)
Rewind drops the last thing the person said and everything that followed it — the whole turn it started: the assistant's replies, its tool calls, and the results those calls returned. It reports what it removed, oldest first, in the same display shape Agent.Transcript uses, so a surface can un-draw exactly the rows it drew.
It is an edit of the CONVERSATION, not of the workspace. Files the dropped turn wrote stay written and commands it ran stay run: this makes the model stop having been told something, which is what a person means when they take back a badly-phrased instruction and type a better one. Nothing pretends the work did not happen.
Both the live transcript and the session file are rewound. The file is append-only, so the cut is journaled as a marker rather than by rewriting history: a {"type":"rewind","dropped":N} line, which a replay applies by dropping the N messages it had accumulated when it reached the line. The count travels in the marker so that a session which rewinds and then keeps working resumes as itself — everything after the marker is ordinary conversation and is replayed as such.
It is now the special case of Agent.RewindAt: the last Turn point in Agent.RewindPoints, which is the newest place in the conversation the person said something. It keeps its own door because it is the rewind that needs no picker — one keystroke for "not that, let me say it again" — but it no longer keeps its own cutting path. THERE IS ONE CUT IN THIS PACKAGE, and every door onto it only chooses where to put it.
func (*Agent) RewindAt ¶
func (a *Agent) RewindAt(index int) ([]DisplayEntry, error)
RewindAt is the generalized cut: it drops messages[index:], journals the drop, and reports what it removed in the same display shape Agent.Transcript uses. Everything Rewind says about a rewind is true of it — the workspace is not touched, the file is not rewritten, a turn in flight is refused rather than waited for.
The index must be one Agent.RewindPoints offered. It is not rounded to the nearest one: a cut is destructive, the caller is a surface that just drew the list, and a picker that is one row off should hear about it rather than have this package quietly remove a different turn. The error names the nearest valid point so the caller can say what it should have asked for.
func (*Agent) RewindPoints ¶
func (a *Agent) RewindPoints() []RewindPoint
RewindPoints is every place this conversation can be cut, oldest first — the list a picker draws and the only indices Agent.RewindAt will accept.
A TURN POINT IS A USER-ROLE MESSAGE, which is where a rewind usually wants to land: it takes back what was said and everything the saying caused. Index 0 is the system message and is never a cut, and a compaction note never appears at all — it is context this package handed to the model rather than something anybody said, and cutting to it would drop a whole resumed conversation while leaving the summary that replaced its beginning. Both exclusions are the ones [Agent.lastTurnStartLocked] already makes; the note is recognized by the marker [foldMarker] writes, not by guessing at its wording.
A STEP POINT IS ANY OTHER MESSAGE BOUNDARY: after an assistant reply, after a tool result. These are the cuts inside a turn, for a person who wants to keep the instruction and drop only the last thing the model did with it.
THE LAW A BOUNDARY MUST OBEY: A CUT MAY NOT SEPARATE A TOOL CALL FROM ITS ANSWER. A retained assistant message whose calls were answered in the dropped tail is a transcript no provider will take — the call is still there, asking for a result that no longer exists — so those boundaries are left OUT of the list rather than repaired at cut time. The alternative, stripping the dangling calls from the retained message, was rejected on two counts. It would make the live transcript say the model never made a call that it really did make, which is a lie about the past that this package refuses everywhere else — a rewind is an edit of the CONVERSATION and never a claim that the work did not happen. And it could not survive a resume: the journal is append-only and its rewind marker is a COUNT of messages dropped from the tail (sessionfile.go), so there is no way to journal a mutation of a message that is being KEPT. A cut only ever drops a tail, in memory and in the file alike, and the points list is exactly the set of tails that can be dropped.
Note that the law is about what the cut CREATES, not about what it inherits. A call that was never answered anywhere — an interrupt that landed between a batch and its results — is dangling before the cut and after it, and no choice of cut point mends or worsens it. Only a call whose answer sits on the far side of the cut disqualifies a boundary.
func (*Agent) RunHarnessRequest ¶
func (a *Agent) RunHarnessRequest(ctx context.Context, name, text, model string) (<-chan Event, error)
RunHarnessRequest runs one named harness on one request and streams the turn it becomes.
It is a TURN and not a side channel: the request is recorded as the person's message, the report as the answer, and the events are the ones every surface already draws. So it refuses what Submit refuses — a closed agent, a turn already in flight, a session past its spend rail — rather than starting a second conversation beside the first.
THE DETECTION REFUSALS DO NOT APPLY. A name that scores nothing, a turn that mentions no cue, a description nobody wrote well: none of it is read here. The person named the harness, and a matcher's opinion about a choice already made would be this surface overruling them.
The model is the same clause the offer carries — a word this install can place, or empty for whatever the runner is built on. A word that resolves to nothing runs the default rather than refusing, which is the offer's law too.
func (*Agent) RunOrchestrate ¶
func (a *Agent) RunOrchestrate(ctx context.Context, goal, model string, capDollars float64) (string, error)
RunOrchestrate launches one adaptive run and returns immediately with its id. It is the ENGINE this package ships: a surface wires it into Config.OrchestrateRunner and gets a session that can also answer the run's gate and draw its frontier, because the run is registered here.
The model is the TURN'S OWN WORD and it outranks everything: named, it is what the planner thinks with and what every node runs on. Named nothing, the two halves resolve their own roles instead ([orchestrateRoleModel]). The cap is dollars, and zero is a run nobody bounded — legal, and never what a turn asks for.
func (*Agent) SendRunNote ¶ added in v0.4.0
func (*Agent) SetAPIKey ¶
SetAPIKey hands the conversation the key it talks with, after the fact.
It exists for one moment: the surface opened on a profile with no key, asked for one on its first screen (internal/tui3's firstrun.go), and the person pasted it — into a session that already exists, holding a client built without one (internal/provider's NewClient states that a keyless client refuses every request until this lands). The config copy is updated too, because every worker this agent spawns — a task node, an audit, a standing firing — is built from `a.config` and would otherwise inherit the empty key the boot had.
A client that cannot take a key — a test double — is left alone rather than refused: the config still records the key, which is what the doubles read.
func (*Agent) SetApprovalPolicy ¶
SetApprovalPolicy replaces the standing gate for the rest of this session.
A NIL NEVER REPLACES ANYTHING. Nil is the configured-nothing case and it means allow everything (Config.ApprovalPolicy states that law), which is exactly the wrong answer for a caller that failed to build a policy: a surface that could not read the settings rows must leave the gate that is already standing rather than open it. So a nil is dropped here rather than stored, and a caller that genuinely wants an ungated session simply never calls this.
It cannot widen a floor, because none of the floors live in a Policy. The critical-command table, the refusal to vouch for a compound line, the degrade on a bash call whose command cannot be read, and the floor under calls that act in the person's name outside this machine are all inside internal/approval and apply to whatever rule set is handed to them. A pushed policy buys exactly what a relaunched one buys and not one rung more.
func (*Agent) SetApprovalPosture ¶ added in v0.4.0
SetApprovalPosture moves this conversation's gate to one posture and writes the word down. It is sticky the way the rung is: the word lands in the session folder's meta.json (placemeta.go) and cmd/codeaf sets it again on resume, so the gate a person opened here is open when they come back.
A CALL IN FLIGHT KEEPS THE GATE IT WAS DECIDED UNDER, which is approvalgate.go's bargain: the policy pointer is swapped, never edited, so a decision already taken was taken about a rule set that really was in force.
A REBUILD THAT FAILS MOVES NOTHING. The word is not stored and the gate already standing stays standing, for the reason Agent.SetApprovalPolicy refuses a nil: the answer to "I could not read the rules" is never a session whose posture and gate disagree.
func (*Agent) SetAutonomy ¶
SetAutonomy is the one door surfaces use for the D-key promise.
func (*Agent) SetContextWindow ¶
SetContextWindow tells the agent how many tokens the model it is now running actually accepts, and the compaction threshold follows it from the next check onward.
It exists because Agent.SetModel does. Config.ContextWindow is the window of the model the session STARTED on; a person who switches to a 1M-token model mid-conversation would otherwise keep compacting at the old model's threshold — reading 128k of a window eight times that size — and one who switches the other way would overflow. A surface that knows the new model's window (the catalog's figure) sets it here alongside SetModel; one that does not, does not call this, and the configured window stands.
Zero and negative are ignored rather than clearing the window: "I don't know this model's size" must not be spelled the same way as "this model has no context", and a caller passing an unknown figure through means the first.
func (*Agent) SetConversationEffort ¶
SetConversationEffort sets this conversation's rung and reports whether the word was one. It is sticky: the rung lands in the session folder's meta.json with everything else about this conversation (placemeta.go), so a person who dials it, closes the terminal and comes back finds it where they left it.
A CALL IN FLIGHT IS UNAFFECTED. The rung is latched at the top of a turn with the model (loop.go), so a change made while the agent is working lands on the next call and never half-way through the one being answered.
func (*Agent) SetModel ¶
SetModel swaps the model, and A PERSON'S WORD WINS AT THE NEXT REQUEST.
A swap made while the agent is working reaches the work through steer.go's one door ([Agent.hearModelLocked], which states the law and the failure it was measured against): the request in flight is cut and asked again on the new model if nothing of it had reached the person, and otherwise the answer they are reading finishes and the next request the work makes carries the new model. It is never the next TURN — a task step is one turn and can run for twenty minutes.
Between two requests the latched value rides every step and retry, so a swap arriving mid-stream cannot send one model the transcript another model was half-way through writing (loop.go).
THE TURN ITSELF MAY STILL MOVE, and this is the one thing that moves it. A step whose stream is cut over and over spends a budget and then hops to the next model in the chain, announced, and the rest of that turn finishes there (loop.go's completeWithRetry). It is a rescue and not a preference: what a person set here is untouched, so the NEXT turn starts on the model they picked, and the only way this field changes is somebody calling this.
AND IT IS WHERE THE PICTURES ARE MADE SAFE. A conversation carrying attached images carries them as base64 in the live transcript, re-sent on every step of every turn after they arrived; swapping onto a model that cannot see would send them to it with no gate in the way, because no image is being attached this turn. [Agent.scrubBlindImagePartsLocked] states the whole rule and its three deliberate limits — the journal is untouched, the swap is one-way, and a model that CAN see is handed everything unchanged.
func (*Agent) SetPlaceMode ¶
SetPlaceMode records the person's own word about how work happens in one place — "here", "directly", "in place", which all mean the same thing: the work happens in that folder itself rather than in a copy of it.
IT IS SET AND NEVER GUESSED, and it is cleared by the same door with an empty word, because a person who said "in place" once and changed their mind has to have a way back. A place this conversation does not refer to is refused rather than invented: the mode is a fact ABOUT a place, so there has to be one.
func (*Agent) SetReasoning ¶
SetReasoning sets the level for the model now in use, for subsequent turns. A turn in flight keeps the level it started with: runTurn latches it once (loop.go), so a change made while the agent is working lands at the next Submit.
AND THAT IS NO LONGER THE MODEL'S RULE. A model a person names reaches the work at the next REQUEST, cutting the one in flight when it has produced nothing they could use (Agent.SetModel, steer.go's THE PERSON'S WORD WINS). The two rules differ because the two acts do: a person changing models is redirecting work they are watching go the wrong way, and a person turning the thinking up is setting a level for the next thing they ask.
WHAT MOVES THE MODEL STILL MOVES THIS. Whenever the step's model changes — a rescue's hop, or the person's own word — the level is re-read for the model now in hand, because a level is a choice about a model and carrying one across would be asking the new model for something nobody set on it.
An unrecognized level is ignored rather than cleared. The two callers are a picker that can only produce the four it draws and a flag the door has already validated with ParseReasoning; between them, a value this does not know is a bug upstream, and answering it by silently dropping a level the person did set would hide it.
func (*Agent) SetReasoningFor ¶
SetReasoningFor sets the level for one model id without switching to it.
func (*Agent) SetSources ¶
func (a *Agent) SetSources(sources modelsource.Set)
SetSources replaces the live service set used by this conversation and by every child it opens later. If the current model's account moved or was removed, the retained provider client is replaced before another request can use the old address or bearer. A running turn keeps the client it started on; startTurnLocked performs the pending replacement for the next turn.
func (*Agent) SetSpendRail ¶
SetSpendRail binds a setting written in an open chat before the next turn or delegated run reads the ceiling. In-flight work keeps its admitted limit.
func (*Agent) SetTaskEffort ¶
SetTaskEffort sets the rung one task's workers run at.
It is checkpointed with the rest of the task (task_store.go writes spec.effort), so a task that outlives the process comes back at the rung it was set to.
Settled ordinary tasks save a separate continuation rung; queued and running tasks update their worker setup. Saving a rung never restarts work.
A WORKER ALREADY RUNNING KEEPS THE RUNG IT STARTED ON, and that is the same contract the model switch has rather than a second mechanism. The child was launched with the rung as its own conversation, so a change here reaches the next worker this task starts — a retry, a repair, a part handed out after the change — and never reshapes a request already on the wire.
func (*Agent) SettleWrites ¶
func (a *Agent) SettleWrites()
SettleWrites waits until everything this session owes a file BEHIND a person's path has landed: the meta.json stamp, the delta reading's told.json stamp, the fix shelf's counters, and the working copy of any folder referred but not yet cut.
IT IS THE ONE EXIT DOOR, and there is one rather than one per owner because a caller closing a session should not have to know which parts of it defer a write. A deferred write's whole risk is a process that stops while one is owed; this is the answer to that risk, and it belongs at Agent.Close and in every test that reads one of those files back.
IT MUST NOT BE CALLED WITH a.mu HELD, and that is the whole of its contract. Every write it waits on takes that lock to read or replace what it is writing — the stamp fills a Meta, the cut appends to a.trees — so a call from inside the lock waits forever on work that is waiting for the caller. In Agent.Close the place is beside [Agent.settleDeliveries] (agent.go), BEFORE the `a.mu.Lock()` that follows it, and nowhere after.
func (*Agent) ShortTitle ¶
ShortTitle is a compatibility alias for older callers. Conversations have one name.
func (*Agent) SkillFacts ¶
SkillFacts is the shelf as THIS SESSION reads it — the same store the catalog, the skills a message carries and `use_skill` read ([Config.skillShelf]) — for a surface that lists it. It is the session's answer and not the surface's because which store the shelf lives in is the door's choice, and a picker that read some store of its own would be a second answer to "which skills can this conversation use".
func (*Agent) SpellOut ¶
SpellOut expands one draft into the block the surface draws under the box.
It is a door beside Agent.SubmitStanding rather than a flag on one, because the two are not the same kind of thing at all: that one SENDS a message, and this one sends nothing — no turn starts, no transcript grows, and the person may well throw the answer away. It is an optional door for that reason too, reached through a small interface in the surface file that uses it (internal/tui3's spellout.go), so a build without it offers nothing rather than offering something that fails.
EVERY FAILURE ANSWERS "", and the caller's only response to "" is to put the hint back. There is no error to return that a person could act on.
func (*Agent) StandingApprovalPosture ¶ added in v0.4.0
StandingApprovalPosture is the posture a conversation NOBODY has touched would open at on this install: the launch's word (`--yolo`) where one was given, else the settings rows as they stand. It is what a draft for a conversation that does not exist yet says about its gate — the rule above the box on home and the other places (internal/tui3's boxseam.go) — and it deliberately ignores this conversation's own stored word, because a pin made in one conversation is not a fact about the next one. "" is a session with no door.
func (*Agent) StandingExcept ¶
StandingExcept records that the named item does not reach this place: the conversation when the item's altitude is wider than it, the workspace when the item is machine-wide. The mirror gesture — excepting a place from the item's own record — writes the same fact.
THE EXCEPTION IS MADE AT YOUR ALTITUDE RELATIVE TO THE ITEM, which is the whole law and the only thing that makes one keypress unambiguous. A machine-wide order excepts THIS PROJECT — "not for this project" is what somebody means when a rule for everything fires wrongly in one repository. A project order excepts THIS CONVERSATION. A conversation order has nowhere narrower to go: it reaches only here, so "not here" would be stopping it altogether, and it says so rather than retiring something quietly under another name.
IT WRITES ONE FACT AND NEVER TWO. Excepting a place already excepted is somebody pressing the key twice, and the answer to that is the exception they already have.
func (*Agent) StandingHere ¶
StandingHere answers the items that stand over this conversation — its own, its project's, and the machine's, in that order, recent first within a shelf — and separately the ones the person excepted from here, so the page can draw its dim "not here" lines. Nil and nil when the ambient side is off.
THE ORDER IS THE STORE'S AND NOT THIS FILE'S (standing.Store.Applicable). A second reading of which shelf an item sits on would be a second answer to the question every seam in this wave asks.
THE "NOT HERE" LINES KEEP THE STORE'S OWN ORDER, newest first. They are a footnote under shelves the page has already grouped by altitude, and a second ordering law for a dim line is a second thing that has to stay true.
func (*Agent) StandingPause ¶
StandingPause pauses an active item or resumes a paused one, and answers the status it now has.
THE ITEM SAYS WHICH WAY THE KEY GOES, so one key does both and the page never has to hold a state of its own. A retired item is not among the two: it is over, and setting it up afresh is a new card.
func (*Agent) StandingStandDown ¶
StandingStandDown retires the named item, at its own altitude, recording that the person stopped it. It is the `stand` tool's own stop, reached from the page instead of from a sentence ([Agent.standingMove]) — one path, so the two gestures can never write the reason two different ways.
func (*Agent) StandingTrees ¶
func (a *Agent) StandingTrees() []StandingTree
StandingTrees is a copy of what this conversation holds, for a surface and for a test. Nothing changes when it is read.
func (*Agent) StartDelegate ¶
func (a *Agent) StartDelegate(ctx context.Context, name, brief string) (uint64, string, string, error)
StartDelegate hands one person-authored brief to the named program. It is `/<name> <brief>`'s door and it answers what StartTask answers: the id the row wears, the title, a note about where the work stands (always empty here) and the error. Nothing is waited for: the run starts and the turn goes on.
The refusals a person can meet, in their own words: a name this build carries no program for, an empty brief, and a build whose run road is not linked.
func (*Agent) StartTask ¶
func (a *Agent) StartTask(ctx context.Context, brief string, solo bool) (uint64, string, string, error)
StartTask starts one person-authored task without routing it through the chat model or presenting the model's proposal card.
── NOTHING IS WAITED FOR IN FRONT OF IT ──
WHAT WAS TRUE: the surface asked the sizing judge (three seconds) and then this door asked the shaper (twenty-five) before the node was admitted, in series, and on a thinking model both ran their windows out and returned nothing — so every `/task` read `shaping the brief…` for twenty-eight seconds and then started on the person's sentence anyway (issue #936).
WHAT IS TRUE NOW: the node is admitted here, at once, on the person's own sentence and the canned done-condition, and both readings run BESIDE ITS FIRST WORKER through the one mechanism every answer beside the work comes through (task_beside.go). The shaper writes the brief and hands it to the worker when it lands (task_shape.go); the judge reads the sentence for width and its parts are weighed as a division the worker is handed (task_divide_sketch.go). Neither decides whether the work may start, and the worker's first request waits on neither.
solo is the person saying the work is one worker's and asking for no reading of its width — `/task solo`, or a standing answer of `single` — and it is the only thing the judge needs to be told not to do.
THE NAME is the one the namer gives every node nothing named ([TaskGraph.nameNode]), asked the moment the node exists; until it lands, the title is the mechanical cut of the person's opening words ([taskPersonTitle]).
WHAT IS STILL THEIRS, WORD FOR WORD, is the summary under the row and the request the worker is told outranks anything a model wrote.
func (*Agent) StartTaskEffort ¶
func (a *Agent) StartTaskEffort(ctx context.Context, brief string, solo bool, effort string) (uint64, string, string, error)
StartTaskEffort is Agent.StartTask with the one-task effort word said: `/task --best` and `/task --cheap`. The word moves this task's crew and nothing after it.
func (*Agent) StateBlock ¶
StateBlock renders the working state as one compact bracketed block, or "" when there is nothing to say.
THIS IS THE SEAM COMPACTION IS BUILT FOR, and it is deliberately a pure function of the store: it takes no lock of the agent's, makes no request, and can be called from inside a compaction pass that is holding whatever it is holding. What the parent does with it is inject it into the REBUILT transcript, right after the summary note, so that the pass hands the model back two different things about the same conversation: a lossy narration of what happened (the summary) and a verbatim record of what is true and what is open (this). The second one is not derived from the first and never passes through the summarizer — that is the entire mechanism (§4: retrieval must not re-ingest its own output).
[state] 2 beliefs · 1 open · 1 done — working state, kept outside the transcript beliefs: - b2 the module path is github.com/Agent-Field/codeaf ← read: go.mod open: - p2 wire StateBlock into the compaction rebuild ← grep: compact loop.go done: - p1 state.json rehydrates on resume ← bash: go test ./internal/session
Beliefs first because they are what the next turn will act on, OPEN BEFORE DONE because the open ones are work and the done ones are history, and newest first inside each because the recent end of a conversation is the live one. Stale beliefs do not appear at all: a belief that stopped being true has no business riding in front of every future turn, and it stays in state.json only so the person can see what was retracted.
func (*Agent) Steer ¶
Steer puts one sentence into the turn that is running.
The returned channel is a live view of that turn from this moment on, exactly as a steering Agent.Submit's is: the caller watches what its correction does rather than being told it was queued and left staring at nothing. Three events on it are this steer's own — EventSteerAccepted at once, then EventSteerConsumed when the model is given the words, or EventSteerFellThrough when the turn ends before a boundary comes.
ON A FALL-THROUGH THE SAME CHANNEL CARRIES ON, into the turn the words then start of their own accord. That is the whole reason the stream is built here rather than taken from the hub: a subscription belongs to one turn and closes with it, and a person whose correction arrived one step too late is owed the answer to it on the channel they are already holding, not on a second one they would have to know to ask for.
ErrNothingToSteer when no turn is in flight — the caller should have sent the message normally, and this refuses rather than silently becoming that.
WORDS ONLY. Pictures reach a running turn by their own door (Agent.SubmitImage, image.go), which assembles parts, reads files and journals durable references; a steer that took attachments would be a second copy of that assembly, and it is not cheap enough to carry along. A surface with a picture to add sends it that way, and it lands at the same boundary.
func (*Agent) SteerOrchestrate ¶
SteerOrchestrate appends one steering note; the planner sees it on its next call. Steering outranks the plan.
func (*Agent) SteerRepeatKnown ¶
SteerRepeatKnown says a send repeated under one SteerSource is taken once.
IT IS ASKED BEFORE ANYTHING IS SENT, because the answer decides what a surface may do with a send it got no answer to: an engine that recognises the repeat can be asked again, and one that does not must not be — a second send there is a second correction, and the person would be the one to find out. This engine answers yes because this engine IS the record that recognises it ([taskAssignment.hear], and the checkpoint that survives a restart carries the identity with the direction — task_store.go's sourceRecord). A client speaking to another machine answers for THAT machine (internal/remote).
func (*Agent) SteerTask ¶
func (a *Agent) SteerTask(id uint64, text string) (SteerReceipt, error)
SteerTask injects the person's words into a running node's loop — the same steering lane a job's exit note rides ([Agent.enqueueSteering]). Unknown id or a node that is not running is an error naming which.
The note carries NO DECORATION. A job's exit is framed ("task 3 finished: …") because the model has to be told what kind of news it is; a person's line needs no frame, because from the child's side it is what it looks like — the person talking. Wrapping it would teach the node to read the person's words as a system event, which is the one thing they are not.
THE RECEIPT THE ENGINE ADDS IS A SECOND MESSAGE AND NEVER A WRAPPER, for exactly that reason: the id a revision has to cite is a fact about the delivery rather than part of what was said, so it is said separately, in the engine's own voice ([taskRoom.steerIn]).
── AND A NODE THAT IS WAITING ON ITS OWN PIECES STILL HEARS IT ──
The first answer is whether the node was WAITING when the line was taken: it has handed part of its work out, said everything it had to say, and parked on the reports (task_run.go's [TaskGraph.park]). Nothing about it looks different from outside — a parked node is a RUNNING node — but for the person it is the difference between an answer in a few seconds and one that reads as silence, so the surfaces say which it was in their own words rather than promising the same thing about two different waits.
It is a fact and not a refusal, because the line does arrive: the parked runner is released by the enqueue below, wakes with the sentence on its queue and re-enters the model with it ([taskRoom.steerIn], [runTaskChild]). Before that it went onto a queue with nothing to drain it — held for as long as the slowest piece ran and dropped outright if the last report arrived first, while the room said it had arrived.
A LINE NOBODY CAN READ ANY MORE IS A REFUSAL AND NEVER A DROP. A node whose worker closed in the instant between the state check and the enqueue — the last piece reported, the parent folded, the agent shut — cannot be talked to, and the person is told so in the same breath as every other "there is nobody in there".
── AND A LINE THE CHECK CANNOT BE SHOWN IS HELD, NOT REFUSED ──
While the gate is reading the work there is nobody inside the node, and until this returned a receipt that was the end of the story: the person was refused and their words stayed in their own box. A correction sent in that window is exactly the one that matters — the work is about to land — so it is TAKEN and written onto the node's own record instead ([TaskNode.heardDirection]), and the landing revalidates against it before anything is published (assignment.go, task_ledger.go). SteerReceipt.Held is how a surface tells that apart from delivery, in the engine's own words.
func (*Agent) SteerTaskFrom ¶
func (a *Agent) SteerTaskFrom(id uint64, text string, from SteerSource) (SteerReceipt, error)
SteerTaskFrom is Agent.SteerTask with that identity on the send. Everything else about it is the same door: the same refusals, the same wake for a parked node, the same hold while the work is being checked, and the same receipt the worker cites to revise.
A SEND WITH NO IDENTITY IS THE OLD DOOR EXACTLY. An empty SteerSource makes this Agent.SteerTask, so a caller that has no way to number its sends is not made worse off — it simply cannot ask again safely, and the surface that cannot is the one that must keep the words and ask the person.
func (*Agent) StillGoing ¶
StillGoing reports that an unattended run has something left to happen. A session somebody is steering, a run with no budget, and a task's own agent all answer false because none of them has a door that should wait here.
func (*Agent) StopWork ¶
StopWork closes this conversation's admission gates before cancelling work. Unlike Interrupt, this includes background jobs and adaptive runs. Completion news remains in the record but cannot buy another turn until a fresh Submit.
func (*Agent) SubharnessIntake ¶
func (a *Agent) SubharnessIntake(name string) (SubharnessCard, error)
SubharnessIntake is the card's data for one subharness: every field of its input schema, what is filled, and which required ones are still blank.
A NAME NOTHING HAS IS AN ERROR AND NOT AN EMPTY CARD, which is the difference between this door and the list beside it. Somebody typed a name; getting a blank card for a subharness that does not exist would send them looking for the fields rather than for the typo.
THE ORDER IS THE SCHEMA'S OWN ([exec.Schema.Fields] orders by `x-order` where the schema states one and alphabetically otherwise), so a card drawn twice is the same card.
func (*Agent) SubharnessList ¶
func (a *Agent) SubharnessList() []SubharnessRow
SubharnessList answers every subharness visible to this conversation, across every layer, in one list with the registry's own precedence already applied.
IT IS THE REGISTRY'S ANSWER AND NOT A SECOND ENUMERATION ([exec.Registry.Manifests]). Compiled-in Go programs and bundles out of a store arrive here indistinguishable, which is the whole contract in one line: the person, the model and this list cannot tell which is which, because there is nothing here that says.
THE GENERALIST IS NOT ON IT. `linear` is registered as a runner — the deoptimization path resolves it by name — but it is what you get when you pick nothing, not something you pick, and a list that offered it would be offering the absence of a choice as a choice.
Nil when this build has no registry, which draws as nothing.
THE TUI LANE draws it; the LastRun note is read through the STORE LANE's seam (Config.SubharnessLastRun), which is filled from the run journals that lane keeps beside each bundle. A build with no seam wired draws no note at all, which is exactly what a subharness nobody has run yet should draw — the emptiness law, and never "0 runs".
func (*Agent) SubharnessRun ¶
func (a *Agent) SubharnessRun(ctx context.Context, name string, input json.RawMessage) (uint64, string, error)
SubharnessRun launches one subharness on the input the card settled, as a task node, and answers the node's id and its title — the same pair Agent.StartTask answers, so a surface that already knows how to open a room on a started task needs no second call site.
A RUN IS A TASK NODE: a roster row, a room, a journal fed by the program's own log() and by the host journal underneath it, an id, and a ✕ that cancels it through the route every other task uses. That fixes today's asymmetry, where DESIGNING a harness is a task and RUNNING one blocks the conversation as a turn (harness.go) — the thing a person most wants to walk away from is the one thing they cannot.
THE TASK SURFACE IS A PRESENTATION OF A RUN AND NOT ITS DEFINITION. The headless path runs the same program through the same runner with no task system in the process at all, which is why the contract this door sits on ([exec.Runner]) knows nothing about tasks and this door knows everything.
THE BODY IS subharness_run.go's, and this is the door onto it: the name is resolved here so that a typo is refused before a node exists to carry it, the id is minted before the node is admitted (that file states why), and the pair that comes back is the pair a surface already knows how to open a room on.
NOTHING IS ASKED HERE, because the question already happened: every path into this door goes through the intake card and somebody confirming it.
func (*Agent) Submit ¶
Submit appends text as a user message and runs one turn: provider requests interleaved with tool execution until the assistant answers without a tool call. The returned channel streams the turn's events and is closed after EventTurnDone or EventError. Submitting while a turn runs injects the message into that turn (steering) rather than starting a second one.
EVERY call returns a live channel, steering included. The in-flight turn owns an event hub, and a steering Submit subscribes a fresh channel to it: the caller sees the turn's events from its own subscribe point onward and the close at EventTurnDone. There is no replay — a surface that submits per message is watching the turn from where it spoke, not re-reading it — and a surface holding both channels sees each event on each, which is what a per-message caller wants and what a whole-session reader must de-duplicate.
func (*Agent) SubmitBash ¶
SubmitBash is the explicit human command door. Generic submissions, including scheduled work and model text, never gain shell authority from a leading !.
func (*Agent) SubmitImage ¶
SubmitImage is Agent.Submit with pictures: it appends one user message carrying the text and the images as content parts, and runs a normal turn — same hub, same events, same journal discipline, same steering rules. A caller that already knows how to read a turn's channel needs to learn nothing new.
A model that cannot see does not end the message: the images go to a VISION MODEL instead, and its answer is the turn's reply ([Agent.visionTurn]). Only when no vision model resolves does the refusal below fire.
It refuses BEFORE anything is recorded when nothing can see the pictures (see Config.SupportsImages) or when the images are too large. A refusal here is an error return rather than an event stream, unlike the spend rail's: the rail refuses a turn the person legitimately asked for and will ask for again, while this refuses a message that was never assemblable — nothing was journaled, nothing was queued, and the person still holds their text.
No images is exactly Submit, so a surface with an empty attachment tray can call one method for both.
func (*Agent) SubmitStanding ¶
SubmitStanding is Agent.Submit for a draft the person MARKED STANDING: the same turn, the same hub, the same events, the same steering rules — with [standingMarkInstruction] in front of the sentence for the model, and the sentence alone for the journal.
A caller that already knows how to read a turn's channel needs to learn nothing new, which is Agent.SubmitImage's bargain and the reason this is a method beside it rather than a flag inside Submit: the two doors differ in what the message is, and in nothing else.
func (*Agent) TakeBackDecision ¶
TakeBackDecision is Agent.HandUnverifiedToModel in reverse: the person deciding, after all, to decide. It is what the card's "take it back" presses, and it is what the floor does by itself at the end of a turn (task_run.go's [Agent.handBackUnsettled]).
IT RESOLVES NOTHING EITHER. The node stays exactly as it is and what changes is who is holding the question, so the card stops saying codeaf is deciding and draws its chips again. A line already on the steering queue is left where it is: the model may still say what it thinks, and what it may no longer do is have the last word.
func (*Agent) TakeoverAsked ¶
TakeoverAsked reports that another window has asked for this session and it has not been let go of yet. It is what a surface that missed the event asks.
func (*Agent) TaskBeat ¶
TaskBeat is where a running node's pulse sidecar lives, as the node itself named it — the same field the checkpoint's own row carries (task_store.go's taskRecord.Beat) — and "" for every node that is not writing one: an unknown id, a finished node whose pulse was taken away (task_beat.go's stop), or a session with no journal and therefore no store to name it.
IT IS ASKED FOR AND NOT RECOMPUTED, for the same reason the record carries the name at all: a reader that built the path from an id would be a second spelling of where the pulse lives, and the two would drift the day the layout moved. A surface over a connection asks the engine for it with the record itself, so the file's name crosses the wire as data.
func (*Agent) TaskContextTokens ¶
TaskContextTokens is what one node's worker weighs right now — the request it has in flight, the figure a node's page draws as ↑ — or zero when nobody is working the node: an unknown id, a node that has not started or has ended, or the moment between one hand and the next.
IT ASKS THE WORKER IN THE ROOM, whoever that is: the same [taskRoom.speaker] the node's live spend is read from ([TaskNode.spend]), so a repair round or a design thread standing in the room is the one whose weight is drawn, because it is the one sending. The graph lock is let go before the worker is asked, for the spend's reason: the worker's answer is a lock of its own.
func (*Agent) TaskEffort ¶
TaskEffort is the rung set on one task, "" when nobody has set one and for a task this session does not have.
func (*Agent) TaskIndex ¶
func (a *Agent) TaskIndex() []TaskIndexEntry
TaskIndex is the project's task index with THIS conversation's live graph merged over it: every landed node the directory has ever recorded, plus the nodes running right now, which by definition are not in the file yet.
THE MERGE IS HERE AND NOWHERE ELSE. Both readers — the "@" drop-up and the `tasks` tool — need running work in the list, and a surface that merged its own copy of the live registry would be a second answer to "what is running" with a different set of rules for keeping it fresh.
func (*Agent) TaskJournal ¶
TaskJournal is the node's journal path — its whole transcript on disk — or "" for an unknown id, and for a node whose transcript cannot be found.
The path is recorded when the node's child agent is built, because that is where it is minted: [taskJournalPath] stamps the current time into the name, so recomputing it later would name a file nobody ever wrote. It survives the process on the checkpoint (task_store.go's taskRecord.Journal), which is what lets a finished task's room replay after a restart.
A NODE FROM A CHECKPOINT THAT NEVER CARRIED THE PATH IS LOOKED UP BY ITS ID. The file is named with the node's id in the session's own journal directory — the same directory [taskJournalPath] mints into — so it is found rather than guessed ([findTaskJournal]); a name that is not on disk answers "" as it always did. What is found is written onto the node and checkpointed, so the lookup happens once per node per life rather than on every open.
func (*Agent) TaskReport ¶ added in v0.4.0
TaskReport is the agent's own account of its work, composed the way a node's landing composes its report: the final assistant message, read off the transcript rather than accumulated from the deltas, because a non-streaming provider emits no deltas and both roads end with the same recorded message ([taskReport] says why at length). It is what the run's supervisor writes when the worker's own `plandb done` has not already ended the task.
func (*Agent) TaskUpdates ¶
TaskUpdates is a standing subscription to every task update this session emits, for the whole life of the session rather than one turn.
It exists because a node's most important event — it finished, here is the report, here is what merged — happens when no turn is running and no Submit channel is open. A surface that draws task cards subscribes once at startup; a surface that does not draw them never calls this and pays nothing.
The channel is never closed by a turn ending — a turn's end is not the end of the work it handed off. A surface holds it for the life of the session and stops reading when it stops drawing.
AND THE FIRST SUBSCRIBER IS HANDED WHAT ARRIVED WHILE THE WINDOW WAS SHUT. The fold was built inside New ([Agent.drainStandingInbox]), where there was nobody to send it to, so it waited here for the surface to open the lane; the stream is unbounded, so handing it over is an append and never a wait. It is handed over ONCE — a second lane on the same session is a second view of the same conversation, not a second person arriving.
EVERY subscriber is handed the task ROSTER, by contrast, not only the first: the rows are facts about the graph rather than news, and a lane opened by a surface with nothing drawn yet — a conversation resumed from its checkpoint, one switched back to behind home — needs all of them to rebuild its column ([Agent.replayTaskRoster]).
func (*Agent) TitleChanges ¶
TitleChanges is the standing subscription to the name this session gives itself, for Agent.HarnessDesigns' reason: the name is now minted beside the turn rather than inside it, so its event very often has no turn stream left to arrive on. A surface subscribes once and holds it for the life of the session.
func (*Agent) ToolOnBelt ¶
ToolOnBelt reports whether this conversation actually carries one tool, by its registered name.
IT IS EXPORTED FOR THE GUARD THAT ASKS IT FROM OUTSIDE. A subharness may declare a cheap precondition — "there is no point running me here unless this tool exists" — and the runtime checks it before spending anything (internal/jsrun's `Look`). The runtime is handed that question by the surface at load time, before this agent exists, so the surface holds one indirection and fills it with this method the moment it does (cmd/codeaf's beltWatch).
IT ASKS THE BELT ITSELF RATHER THAN A LIST, so a tool armed for an account this conversation connected five minutes ago is found (connect.go's armFamily). The whitelist is a different question and is asked elsewhere: the whitelist is a ceiling the program declared, and this is what the machine really has.
func (*Agent) Transcript ¶
func (a *Agent) Transcript() []DisplayEntry
Transcript returns the conversation so far as display entries, oldest first. It exists for replay-on-resume: the surface renders the tail instead of opening on an empty screen. The system prompt is never included; a compaction summary note is.
func (*Agent) Typing ¶
func (a *Agent) Typing()
Typing says a person has started writing a turn.
IT IS THE ONLY THING IN THIS PACKAGE A KEYSTROKE REACHES, and it exists because of what a keystroke KNOWS: seconds before a request is made, roughly where it will go. A one-token request to the two lanes at the head of the frontier costs about two hundredths of a cent, lands in the ledger as a measurement of OUR path taken NOW rather than everybody's average over half an hour, and warms the connection so the real request's first token is not also paying for a handshake (internal/lane's probe.go).
NOTHING WAITS FOR IT AND NOTHING CAN BE MADE SLOWER BY IT. It returns before anything is sent, the debounce is the prober's own — at most one pair every twenty seconds per model — and every gate that could refuse is asked inside it. A surface may therefore call this on every character typed, which is what internal/tui3 does, because a first keystroke is not a thing a composer can tell apart from a fifth.
It is silent for a session with a task's posture: a node's turn is not somebody typing, and an errand's pane is not the room they are sitting in.
func (*Agent) UnlandedChanges ¶
func (a *Agent) UnlandedChanges() []StandingChange
UnlandedChanges is what the composer draws: one row per folder with work waiting in it, newest cut first. THE EMPTINESS LAW LIVES HERE — a folder with a copy but nothing written into it is not news and answers nothing, so the chip appears exactly when there is something to land.
func (*Agent) UnqueueFollowUp ¶
UnqueueFollowUp takes ONE queued follow-up back out, named by the stream the surface has held since the moment it queued — the stream is the receipt, and matching on it rather than on a position is what keeps a queue the surface cannot see whole (a steering line queued as a follow-up at a turn's end, steer.go) from making an index lie. It reports whether the message came out.
FALSE IS THE RACE, SAID HONESTLY. A turn that ended between the surface's frame and this call has already drained the message and started its turn, and a false here is the surface's sign that it may not call the words its own again — the turn is the person's whether they wanted it or not, and taking the row off the screen while the model answers it would be the surface claiming a removal that never happened.
The stream is closed on the way out, exactly as [Agent.dropFollowUpsLocked] closes each one: the channel ending with no events is how a caller reads "this never ran", and a message taken back before dispatch must read exactly that way — it never executes.
func (*Agent) Usage ¶
Usage returns the session's accumulated usage.
IT IS NOT A SECOND SET OF BOOKS. Every figure in it was put there by [Agent.bank] — the one door a call's money goes through — and that same call hands the machine's ledger its row (usage_ledger.go). So this and the ledger are one total kept by one owner and a tail read of the file, never two accumulators that can drift: the status line, the day's reading and the spend place cannot disagree by construction (issue #269). It is a running total rather than a scan of the file because a surface asks this on the frame clock and a ledger with a year in it must not be walked per frame.
The one place it is deliberately LARGER than this session's own ledger rows is a fold: a child agent wrote its own calls down under its own id, and folding its tally home moves these books and writes nothing there (the fold rule). UsageTree is how a surface reads the family back out of the ledger without counting the same call twice.
func (*Agent) WaitingOn ¶
WaitingOn is the one line about WHY, and it is exactly what the presence file writes into SessionPresence.Reason — the same call, at the same instant, so a row drawn from a live agent and a row drawn from its file say the same thing.
It is "" both when nothing is being asked and when the lane that raised the question had no words for it, and a surface must draw nothing at all in either case rather than a placeholder (the emptiness law).
func (*Agent) Wakes ¶
Wakes is the standing subscription to turns THE SESSION STARTED ON ITS OWN: one channel per woken turn, handed over before that turn's first event, and closed when it ends — the same shape Agent.Submit returns, because it is the same thing.
It exists because a wake has no caller. Every other turn is somebody asking for something and reading the answer off the channel they were given; a turn started by a task landing is the model speaking to a room nobody is holding a microphone into, and without this the answer would reach the journal and never the screen. A surface adopts each stream exactly as it adopts a follow-up's (internal/tui3's followup.go): draw the turn, pump to close.
The lane is buffered and NEVER BLOCKS the session: a subscriber that has stopped reading misses wakes rather than freezing the agent that is trying to tell it something. It closes with the session.
func (*Agent) WatchHarnessDesigns ¶
WatchHarnessDesigns is Agent.HarnessDesigns with a way to stop, for Agent.WatchTaskUpdates' reason and on its terms: same subscription, stop takes the watcher off the list and ends its pump, never nil, and calling it twice is calling it once.
func (*Agent) WatchOrchestrations ¶
WatchOrchestrations is Agent.Orchestrations with a way to stop, for Agent.WatchTaskUpdates' reason and on its terms: same subscription, stop takes the watcher off the list and ends its pump, never nil, and calling it twice is calling it once.
func (*Agent) WatchQuestions ¶
WatchQuestions is a standing subscription to every question this session raises, withdraws and has answered, for the whole life of the session rather than one turn. stop is never nil and calling it twice is calling it once.
IT IS A LANE OF ITS OWN AND NOT THE TASK LANE, and that is deliberate rather than tidy. Agent.WatchTaskUpdates is the ROSTER's lane: a surface holding it reads a strict sequence of task rows — the roster replayed on open, then one notice per move — and a question threaded into that sequence is an event that lane's readers have to skip past to find the row they were waiting for. They are two different subscriptions because they are two different things: what the work is doing, and what somebody is being asked.
It exists because most questions outlive the turn that raised them or never had one. A landed task waits on somebody's word with no turn running at all, a question is withdrawn on the presence heartbeat, and an answer left in another window arrives on that same beat — none of those has a hub to speak on, and a surface that only read turn streams would never hear them.
func (*Agent) WatchTask ¶
WatchTask subscribes to one node's LIVE event stream: the child agent's own events — text deltas, tool begins and ends, errors — as they happen, opening with the step the node is in the middle of ([taskCatchup]). The channel closes at the node's final state and on Agent.Close. Unknown id is an error.
A FINISHED NODE ANSWERS WITH A CLOSED CHANNEL rather than an error: the id is real, the work is over, and a channel that closes immediately is how every other stream in this package says "that is the whole of it" ([eventHub. subscribe] does exactly this for a turn that has ended). The history is the journal; this door is only ever the present.
func (*Agent) WatchTaskRoom ¶
WatchTaskRoom is Agent.WatchTask with a way to stop.
It is the same door onto the same room — the step in flight, then the node's events from now on — and stop is a watcher walking out: the room stops publishing to it, its pump ends and its channel closes. A surface needs it because a person leaves a node's page long before the node leaves, and because a surface that shows one conversation at a time detaches from every node it was watching in the ones behind (see [taskRoom.leave]).
It is a SECOND DOOR rather than a changed one, for Agent.WatchTaskUpdates' reason. stop is never nil, and calling it twice is calling it once.
func (*Agent) WatchTaskUpdates ¶
WatchTaskUpdates is Agent.TaskUpdates with a way to stop.
It is the same standing subscription; stop takes the watcher off the session's list and ends its pump. A surface that keeps several conversations alive and shows one at a time needs it: without a way off the list, detaching leaves a queue the session keeps filling and a goroutine parked on a channel nobody will read again (agent.go's [eventStream.leave]).
It is a SECOND DOOR rather than a changed one because Agent.TaskUpdates' shape is the one internal/tui3 declares in its own interface.
stop is never nil and calling it twice is calling it once.
func (*Agent) WatchTitle ¶
WatchTitle is Agent.TitleChanges with a way to stop, on Agent.WatchHarnessDesigns' terms: same subscription, stop takes the watcher off the list and ends its pump, never nil, and calling it twice is calling it once.
A NAME ALREADY MINTED IS REPLAYED TO THE NEWCOMER, which is the ordinary case rather than a corner: a conversation left in the background is named while nobody is subscribed to it, and a surface that came back to a session with a name would otherwise draw the person's opening words under a session that has been properly named for an hour.
func (*Agent) WatchWakes ¶
WatchWakes is Agent.Wakes with a way to stop. It is the same subscription — one channel per woken turn, buffered, never blocking the session — and stop takes it back off the session's list and closes it, so a surface that has detached from this conversation is not a lane the next wake has to try to send into.
It is a SECOND DOOR rather than a changed one because Agent.Wakes' shape is the one internal/tui3 declares in its own interface, and a surface moves onto this when it has something to do with the stop.
stop is never nil and calling it twice is calling it once. The closed channel it leaves behind is the same thing the session's own close hands every wake lane, so a reader needs no second rule for "this lane has ended".
func (*Agent) WithdrawQuestion ¶
func (a *Agent) WithdrawQuestion(kind QuestionKind, token, reason string)
WithdrawQuestion takes one question back with a reason, and says so.
It is safe to call for a question nobody remembers — a second withdrawal, a lane that never banked its words — and does nothing then, on Agent.ResolveConsent's terms: the thing is already gone, and there is nobody left to tell.
func (*Agent) WorkingNow ¶
WorkingNow is the session's live work as one tree: the session's tasks at the top, their workers beneath, born children included the moment they exist. An idle session answers nil, and nil renders as nothing — the emptiness law.
── WHAT IS IN THE TREE AND WHAT IS NOT ──
A ROOT IS DRAWN ONLY WHILE SOMETHING UNDER IT IS ALIVE, and once it is drawn EVERY worker under it is drawn, settled ones included. The two halves of that rule are one decision. A session accumulates finished tasks for as long as it runs, and a tree that kept all of them would grow without bound and would say "working right now" about work that finished an hour ago — the roster is where history is read. But a task that split into three parts and has two of them home is a board with three rows on it, and dropping the two that landed would leave a person looking at the one part still going with no way to see it was ever a division. So liveness decides the ROOT and the root decides the family.
AN ID IS SOMETHING A SURFACE CAN ACT ON. The spellings are cancel.go's, the one place in this package where both kinds of work already share a vocabulary: `task:4` for a node of the task graph, `run:2` for an adaptive run, `run:2/node-a` for one planned node inside one, and `job:3` for a background job. A reader can hand any of them except the planned node straight to Agent.Cancel; nothing else in this package mints an id that means two different things depending on which kind of work you thought you were looking at.
BORN IS WHEN THE WORKER STARTED, and it is deliberately zero for a worker that has not started yet — one still waiting for a free hand has no age to draw, and zero renders as nothing by the emptiness law. An adaptive run's planned nodes carry no start time at all in orchestrate.Snapshot, so they answer zero for the same reason: the honest answer to a question the engine cannot answer is nothing.
NEVER FAKE LIVENESS. Every row here is a worker this process is holding — a node in the graph or a node on a run's own snapshot. Nothing is invented for a shape that has not been admitted yet. A BACKGROUND JOB IS A HAND AT WORK. It is not an agent and it has no worker under it, but the question this door answers is "is anything happening", and a nine-minute command running under `bash background:true` is the plainest possible yes. It joins the tree for the same reason it joins the roster (jobrow.go): the door that started the work does not change where the work shows. AND A RUN OF THE BELT IS WORK, which is the plainest statement this door can make and was for a long time the one it did not make. A run's rows are kept beside the graph rather than in it ([TaskGraph.keepRunRows]), so the walk over the graph's own nodes never saw them, and a conversation whose ONLY live work was a run read as idle. What reads this answer is the engine deciding whether to retire a conversation (internal/remote's workingNow), so the run that was invisible here was a run whose conversation was retired out from under it, mid-run, at thirty minutes. Saying so here is what keeps the engine alive while a run lives; nothing else had to change to get that.
type AgentKind ¶ added in v0.3.0
type AgentKind string
AgentKind is what an agent IS, in the one question a seat needs answered: whose turns are these?
const ( // AgentChat is a conversation somebody sits in — and every agent that is // neither a node, a checker nor a repair round, because that is what they // all are: a session answering for itself. AgentChat AgentKind = "chat" // AgentTask is one task node's runner (task_run.go). AgentTask AgentKind = "task" // AgentAudit is the read-only judge a node's landing waits on // (task_audit.go's [Agent.newAuditAgent]). AgentAudit AgentKind = "audit" // AgentRepair is one repair round's fresh worker // (task_audit.go's [Agent.repairNode]). AgentRepair AgentKind = "repair" )
type Answer ¶
type Answer struct {
// Clarify asks for context without resolving the pending decision.
Clarify bool `json:"clarify,omitempty"`
// At is when it was given. It orders a drain and is the only thing here a
// reader could use to notice an answer that sat on the doorstep for a week
// — nothing does, because a question that old is one nobody is waiting on
// and the resolvers already drop it.
At time.Time `json:"at"`
// Kind and ID name the question, exactly as the presence file's
// [PresenceQuestion] spelled them.
Kind QuestionKind `json:"kind"`
ID uint64 `json:"id"`
// Key is the answer, as [AnswerOptions] names it.
Key string `json:"key"`
// From is where it came from — "home" is the only writer today. It is here
// so a later reader can tell an answer somebody gave on another screen from
// one a machine gave, without guessing from a timestamp.
From string `json:"from,omitempty"`
// Ref names the question where its lane's token is a STRING rather than a
// number — a connect account, an adaptive run. Exactly one of ID and Ref is
// set, exactly as on [Question].
Ref string `json:"ref,omitempty"`
// Ask is the shape of the decision this answered ([AskKind]). It is
// carried so a record can be read without the question beside it, and it is
// empty on a line an older window wrote.
Ask AskKind `json:"ask,omitempty"`
// Picked are the answers given, by key. It is one key on most questions,
// several on a checklist, and one per row on pairs. Key above is the FIRST
// of these, kept filled so an older reader — and [Agent.applyAnswer]'s own
// mapping — goes on working unchanged; [Answer.Keys] is how this package
// reads either.
Picked []string `json:"picked,omitempty"`
// Labels are the words on the answers in Picked, in the same order. They
// are written only on the copy of an answer handed back to the asker that
// raised the question through `ask` (tools_ask.go), so a model reads what
// `2` meant without a table of its own; the record keeps the keys.
Labels []string `json:"labels,omitempty"`
// Change is what was said BESIDE the pick: "2, but keep the sqlite file as
// the source of truth". It is the half of an answer that carries the
// person's intent, and a lane that can take words does something with it —
// the connect lane reads it as the key it asked for, the landing lane sends
// it to the work as a steer, and the rest keep it in the record.
Change string `json:"change,omitempty"`
// Comments are what was said about ONE answer or ONE blank, keyed by its
// key or its label. They are notes on the parts and never the answer
// itself.
Comments map[string]string `json:"comments,omitempty"`
// AskedBack are the rounds of asking back, bounded at one per answer
// ([Exchange] says why).
AskedBack []Exchange `json:"askedBack,omitempty"`
// Blanks are the fields of a small form, keyed by [Blank.Label].
Blanks map[string]string `json:"blanks,omitempty"`
// Dial is the number a dial was left on, and nil where there was no dial —
// which is not the same as a dial left at zero.
Dial *float64 `json:"dial,omitempty"`
// Reframe is an answer that is not a pick at all: "the real question is…".
// It resolves nothing by itself; it goes back to the asker.
Reframe string `json:"reframe,omitempty"`
// DecidedBy is who answered. It is the field that makes the record worth
// keeping, and its zero value is empty rather than [DecidedByPerson] —
// claiming a person pressed a key nobody pressed is the one thing a record
// must never do.
DecidedBy DecidedBy `json:"decidedBy,omitempty"`
// Scope is how long this answer lasts, and it is only ever one the question
// offered. Empty is [ScopeOnce].
Scope AnswerScope `json:"scope,omitempty"`
// Why is the person's own reason, asked for softly when they answered
// against the pick. It is what a preference is later written from, and it
// is empty far more often than not.
Why string `json:"why,omitempty"`
// TakingOver says a question a running sub-harness asked was answered by
// the person taking the work over rather than by an answer to it. It is
// meaningful on [QuestionSubharnessAsk] alone.
TakingOver bool `json:"takingOver,omitempty"`
// Revises says this is a NEW ANSWER TO A QUESTION ALREADY SETTLED — the
// person changing their mind from the receipt — rather than a second click
// on a question somebody else has already answered.
//
// IT IS THE ONE BIT THAT TELLS THOSE TWO APART, and without it the second is
// what every late answer looks like: answers.go's own law is that a late
// answer is ignored and nothing says so, which is exactly right for a key
// pressed in another window a moment too slowly and exactly wrong for
// somebody who has just read the receipt and decided otherwise.
// [Agent.ResolveQuestion] takes it to the lane's own revise, which refuses
// where a decision cannot be walked back.
Revises bool `json:"revises,omitempty"`
}
Answer is what somebody said to a question: one line of answers.jsonl, and the value every resolver in this engine is reached through (Agent.ResolveQuestion).
IT GREW RATHER THAN BEING REPLACED, and the four fields it started with are still the four an older window writes: At, Kind, ID and Key. Everything below them is `omitempty`, so a build of any age reads a line written by a build of any other — AnswerFromKey still answers from a bare key alone, which is exactly what home has always sent.
WHAT THE NEW FIELDS ARE FOR is the half of an answer a key could never carry: several picks rather than one, the sentence somebody added beside their pick, the blanks they filled, what they asked back, and — the one that makes the record worth keeping — WHO decided and how long it lasts.
func DrainAnswers ¶
DrainAnswers reads and removes a session's answers, oldest first. A folder with nothing on its doorstep is an empty slice and no error, which is the ordinary case on every beat of every session that was never answered from anywhere.
func (Answer) FirstKey ¶
FirstKey is the single key a two-or-three-answer question was answered with, and "" for an answer given in words.
func (Answer) Keys ¶
Keys is what was picked, however the answer spelled it: Answer.Picked where it is filled, and the single Answer.Key where an older window wrote one.
IT IS THE ONE READER OF BOTH, so no lane has to know which shape it was handed. An answer with neither is a real answer on the kinds that take words instead of keys — a clarification, a question a sub-harness asked — and comes back empty rather than as a guess.
type AnswerAction ¶
type AnswerAction struct {
// Kind is the lane this action belongs to, and the field a caller switches
// on to know which of the three below to read.
Kind QuestionKind
// Allow and Scope are the consent lane's answer, for
// [Agent.ResolveConsentRemember].
Allow bool
Scope ConsentScope
// Task is the proposal lane's answer, for [Agent.ResolveTask].
Task TaskAnswer
// Standing is the standing lane's answer, for [Agent.ResolveStanding].
Standing StandingAnswer
}
AnswerAction is what one key MEANS: the answer, in the shape the lane's own resolver takes.
It is one struct with three lanes' answers on it rather than three functions, because a caller with a key and a kind in its hand wants ONE call — and because the zero value of each field is already that lane's "no", so a mis-typed key can never come out as a yes.
func AnswerFromKey ¶
func AnswerFromKey(kind QuestionKind, key string) (AnswerAction, bool)
AnswerFromKey is the whole mapping, and it is the one place it is written.
A key the kind does not take answers false and is applied to nothing. That is the same conservative reading Agent.ResolveConsentRemember takes of an unknown scope, and for the same reason: a typo must never widen an approval, and a key that fell off a chip row must never be read as the answer next to it.
WHAT "ALWAYS" IS, EXACTLY. It is ConsentToolSession — the same answer the window's `a` key sends the engine, which stops that session asking about that TOOL for the rest of its life. It is not ConsentRule: a banked rule is written from the command line the call actually carried, and a window answering somebody else's question has only the one line the session is stopped on. Writing a rule from that gloss would bank a standing approval for a command that was never run (tui3's [app.askCommand] states the same law). The floor holds either way — the shapes internal/approval always asks about are asked again whatever memo is standing.
type AnswerOption ¶
type AnswerOption struct {
// Key is what a person presses. It is a digit on every kind, because the
// surface that offers these is home, where the letters are already typing.
Key string `json:"key"`
// Label is the answer in the words the card uses for it.
Label string `json:"label"`
// Body is what this answer MEANS, in a sentence or two — what the room
// draws under the word when the answer is opened. Empty is an answer whose
// word says the whole of it.
Body string `json:"body,omitempty"`
// Consequence is what happens if this one is taken, in the future tense and
// in one line: "the file is overwritten", "nothing runs". It is the line a
// card draws beside the word, and it is the difference between choosing and
// guessing.
Consequence string `json:"consequence,omitempty"`
// Safe marks THE ANSWER THAT CHANGES NOTHING. A confirmation starts its
// cursor on it and a destructive answer never shares a key with it
// (tabclose.go's law, stated once for every kind). At most one answer on a
// question is safe; where none is, none is marked, because inventing one
// would put a person's cursor on an act.
Safe bool `json:"safe,omitempty"`
// Widening marks an answer that grants MORE than the question asked about —
// "always", "every command like this". It is drawn apart from the others so
// that a person who meant "yes, this once" cannot land on it by muscle
// memory.
Widening bool `json:"widening,omitempty"`
// Blocks are this answer's own evidence: the diff it would produce, the
// layout it would draw, the rows it would write.
Blocks []Block `json:"blocks,omitempty"`
// Dimensions are the axes the asker wants these answers compared on —
// "speed", "what it costs", "what it breaks" — keyed by the axis and valued
// with this answer's reading of it. A question whose answers all carry the
// same axes can be laid side by side; one whose answers do not is drawn as
// a list, and no axis is ever invented to fill the table.
Dimensions map[string]string `json:"dimensions,omitempty"`
}
AnswerOption is one answer a question will take: the key that gives it and the word for it.
func AnswerOptions ¶
func AnswerOptions(kind QuestionKind) []AnswerOption
AnswerOptions is what one kind of question may be answered with, in the order the chips are drawn.
THE KEYS ARE THE CARD'S OWN DIGITS WHERE THE CARD HAS DIGITS. The standing card is answered 1 yes, 2 change when or where, 3 just once in its own window (tui3's standing.go), and 1 and 3 mean the same here — the hand that learned them there is right here. `2 change when or where` is deliberately NOT on this list: it is a request for a text box, and there is no box on the row this is drawn beside.
AND THE WORDS ARE THE CARD'S OWN TOO. A person reading `just once` on the card and `once, not standing` on home would be reading two names for one answer, and the second of them is written in this build's vocabulary rather than theirs — "standing" is a word they never said. One answer, one spelling, wherever it is drawn.
AND `0 not set up` IS THE OUTRIGHT NO, ON EVERY SURFACE THAT DRAWS THE CARD. In the conversation the no was `esc` alone, which home does not have to give — esc there closes home — so a standing card met from home used to offer a yes, a once, and no way at all to say no; the only ways out were opening the window or leaving the question open. StandingNoKey is why the key is a `0` and not a fourth digit.
THIS IS THE ANSWER FOR THE KIND AND NOT FOR AN ITEM. A one-off reminder's card offers no `3` at all, because doing that action "now" is meaningless — StandingOptions narrows this list to one item, and that is what a card and a presence file are actually built from.
THE CONSENT KEYS ARE NEW AND THE ANSWERS ARE NOT. In its own window the gate is answered y / a / n; those letters cannot be borrowed here, because a letter on home is a character being typed. So the three answers keep their meaning and take digits, and the words beside them are the card's own.
func HarnessOptions ¶
func HarnessOptions(ask AskKind) []AnswerOption
HarnessOptions is what one of the harness lane's two questions may be answered with, and the SHAPE OF THE DECISION is what tells them apart.
IT IS THE SHAPE AND NOT A SECOND KIND. [Agent.harnessQuestion] already reads a finished page as AskJudgement and an offer as AskPermission — "a design is a judgement and not a permission: the page is written, and what is being asked is whether it is right" — so asking the shape is asking the one fact that has already been decided, rather than minting a lane whose answers travel back through the same resolver anyway.
THE WORDS ARE THE CARD'S OWN. A design card in the conversation said `[enter] save · [e] change it · [esc] drop` before this list existed, and a person who learned those three verbs there must read the same three here.
func StandingOptions ¶
func StandingOptions(item standing.Item) []AnswerOption
StandingOptions is the answers THIS card offers, in the order they are drawn.
THE WORDS ARE THIS ITEM'S. A reminder, a repeating check, a watch and a rule are different questions, and a label that could mean two of them is how a person presses the wrong one. Every surface, including another window and the record of what was pressed, reads this list.
THE KEYS DO NOT MOVE. 1 is the yes, 3 is once, 0 is the no. Once is missing on a reminder and on a rule, and it is never the answer a cursor may rest on. The no is last and marked safe, so a row that has to drop an answer drops one in front of it.
type AnswerScope ¶
type AnswerScope string
AnswerScope is HOW LONG an answer lasts, and it is the person's to choose among the scopes the question offered.
IT IS NOT ConsentScope, and the two are spelled apart on purpose: that one is the approval gate's own vocabulary for how a tool memo is banked, and it has a third value ("rule") that is about where the answer is written rather than how long it lives. This one is the question object's, and every lane speaks it.
const ( // ScopeOnce answers this question and nothing else. It is the zero value. ScopeOnce AnswerScope = "once" // ScopeTask answers every question of this shape for the rest of this piece // of work. ScopeTask AnswerScope = "task" // ScopeProject answers it for this project. ScopeProject AnswerScope = "project" // ScopeAlways answers it everywhere, from now on. It is only ever OFFERED, // never assumed, and a row answered by one says so with a way to change it. ScopeAlways AnswerScope = "always" )
type ApprovalGate ¶ added in v0.4.0
type ApprovalGate interface {
// Build is the gate for one posture: the finished policy and whether the
// guardian stands in. An empty posture is the rows exactly as they stand.
Build(posture string) (*approval.Policy, bool, error)
// Standing is the posture the rows as they stand amount to, in the ladder's
// own words — what an untouched conversation on this install is running at.
Standing() string
}
ApprovalGate is the door onto the settings rows, handed in by whoever built the session (cmd/codeaf's chatv3_approval.go).
type Artifact ¶
type Artifact struct {
// Path is where the deliverable landed, absolute. The row does not
// promise the file still exists — a picker verifies before it offers.
Path string `json:"path"`
// Session is the 16-hex id of the conversation that produced it.
Session string `json:"session,omitempty"`
// Title is what a picker row says: the export's name, the image's prompt
// slug, the document's first heading.
Title string `json:"title"`
// Kind names what it is — "image", "export", "document" — a word for a
// glyph, not a taxonomy.
Kind string `json:"kind,omitempty"`
// Created is when it landed; every ordering in this file is on it.
Created time.Time `json:"created"`
}
Artifact is one deliverable as the index remembers it.
func ArtifactsSince ¶
ArtifactsSince is the deliverables at path that landed AFTER since, newest first — the files made while a person was away, read against the look stamp (LastLook). path is the index file itself, exactly as ReadArtifacts takes it.
A ZERO STAMP ANSWERS NOTHING, never everything. It is a machine with no origin yet, and look.go's rule for that is that the first look marks nothing as news — a ledger that called every file ever made "since you left" would be the one screen on the machine telling a person something false about their absence.
func ReadArtifacts ¶
ReadArtifacts reads the rows at path, NEWEST FIRST, and tolerates everything: a missing file is a person who has never made anything and answers nil; a line that does not parse, or parses into a row with no path or title, is skipped.
type AskKind ¶
type AskKind string
AskKind is what SHAPE of decision is being handed over. It decides how a question is drawn, what a safe answer is, and whether anything but a person may answer it (docs/design/questions/DESIGN.md's table of kinds and their defaults).
It is a string and not an iota for the reason QuestionKind is: these values travel in presence files and answer files that a build of a different age reads, and a number whose meaning moved when somebody inserted a constant is the one bug a wire format must not have.
const ( // AskPermission is "may this happen": the approval gate, a connect offer, a // harness offer, a sub-harness proposal. Its safe answer is to skip it, and // it may run on a clock only where the stakes are reversible. AskPermission AskKind = "permission" // AskChoice is "which of these": several answers the asker has already // thought through, usually with a pick among them. AskChoice AskKind = "choice" // AskJudgement is "is this good enough" — a landed task's work, a design's // shape. Nothing but a person ever answers one; its safe answer is to keep // what is already there. AskJudgement AskKind = "judgement" // AskClarification is "I could not tell what you meant". Free text is the // FIRST door on this kind rather than the last, because the whole content // of the answer is words the asker did not have. AskClarification AskKind = "clarification" // AskConfirmation is "this is about to happen and it cannot be taken back". // It never runs on a clock, its safe answer is the one that changes nothing, // and the destructive answer never shares a key with a routine one. AskConfirmation AskKind = "confirmation" // AskLanding is a landed task's `your call` — work that finished and that // nobody could check (docs/design/task-states/DESIGN.md's third tier). Its // answers keep that design's keys exactly: `a` accept, `n` do not, `s` tell // it something. AskLanding AskKind = "landing" // AskAssumption is the second rung of the ladder drawn as a question: the // asker states what it is taking for granted and goes on unless somebody // strikes one. Everything on it stands until it is struck. AskAssumption AskKind = "assumption" // AskRatify is the third rung: something reversible was DONE, and this is // the chance to unwind it. Nothing waits on the answer, which is what makes // it the cheapest question on the ladder. AskRatify AskKind = "ratify" )
func (AskKind) Waits ¶
Waits reports whether a question of this shape STOPS ANYTHING. Every kind does but one: a ratify says something reversible was already done and offers the chance to unwind it, so the asker carries on the moment it is shown and the person answers it — or does not — in their own time.
IT IS READ AT BOTH ENDS AND IS THE ONE READING. The lane that raises the question parks a call on it only where something waits (tools_ask.go), the presence file counts only those as this session being stopped on somebody (taskpresence.go's [Agent.waitingOnPerson]), and a surface counts the same ones in the line that says how many questions are open — a `? 1 question` beside a row that settled itself is a person told they are needed when they are not.
type Asker ¶
Asker is who put the question, and the name that goes with it where there is one. The name is EMPTY for the engine and the model, because "codeaf asks" and "the model asks" are already whole sentences and a name after them would be a second attribution of one asker.
type AskerKind ¶
type AskerKind string
AskerKind is WHO is asking, which is the attribution a surface draws dim beside the head. It is never machinery vocabulary: a person reads "the model asks", "codeaf asks", or the task's own name.
const ( // AskerModel is the model, through the `ask` tool (lane E2 owns that door). AskerModel AskerKind = "model" // AskerEngine is codeaf itself: the approval gate, the fuel gate, a merge // conflict — questions no model chose to ask. AskerEngine AskerKind = "engine" // AskerTask is one task node, and [Asker.Name] is the task's title. AskerTask AskerKind = "task" // AskerSurface is a window's own confirmation — stopping a run, closing a // tab — raised by the surface rather than by the engine. AskerSurface AskerKind = "surface" // AskerWindow is another window on this machine, and [Asker.Name] is the // machine or window it came from. AskerWindow AskerKind = "window" )
type Blank ¶
type Blank struct {
// Label is the field's name in the asker's own words.
Label string `json:"label"`
// Kind is what goes in it.
Kind BlankKind `json:"kind,omitempty"`
// Default is what it holds before anybody types, and "" is an honestly
// empty field rather than a placeholder to be invented.
Default string `json:"default,omitempty"`
// Choices are the words a BlankChoice offers, and are empty on every other
// kind.
Choices []string `json:"choices,omitempty"`
}
Blank is one field of a small form: what it is called, what goes in it, and what it already holds. THE DEFAULT IS AN ANSWER ALREADY GIVEN — a person who changes nothing has answered the question, which is the whole reason a form beats a free-text box on the ladder.
type BlankKind ¶
type BlankKind string
BlankKind is what one blank in a small form holds, so a surface can offer the right completion for it rather than a bare box.
const ( // BlankText is words. BlankText BlankKind = "text" // BlankPath is a path on this machine, which a surface may complete. BlankPath BlankKind = "path" // BlankNumber is a number. BlankNumber BlankKind = "number" // BlankChoice is one of [Blank.Choices]. BlankChoice BlankKind = "choice" // BlankTime is a moment or a duration, in the words a person would say. BlankTime BlankKind = "time" )
type Block ¶
type Block struct {
Kind BlockKind `json:"kind"`
// Title is one short line above it, and "" for a block that speaks for
// itself (the emptiness law: nothing is drawn for an empty title).
Title string `json:"title,omitempty"`
// Body is the text, the diagram's lines, or the diff.
Body string `json:"body,omitempty"`
// Rows is a table, header first.
Rows [][]string `json:"rows,omitempty"`
// Path is where a picture is, for BlockImage.
Path string `json:"path,omitempty"`
}
Block is one piece of evidence. Only the field its kind names is filled; the rest are empty, and a reader that does not know a kind draws Title and nothing else rather than guessing at a body it cannot read.
type BlockKind ¶
type BlockKind string
BlockKind is one sort of evidence an asker may attach to a question or to one of its answers. THE EVIDENCE SETS THE SIZE OF THE DRAWING: a question with a diff on it is not a question that fits on a line.
const ( // BlockText is prose. Body is the whole of it. BlockText BlockKind = "text" // BlockDiagram is a drawing the asker made, already rendered to lines. BlockDiagram BlockKind = "diagram" // BlockTable is Rows, the first of which is the header. BlockTable BlockKind = "table" // BlockDiff is a unified diff in Body, drawn with the diff glyphs. BlockDiff BlockKind = "diff" // BlockImage is a picture at Path, which is a path this machine can open. BlockImage BlockKind = "image" // BlockLayout is a rendering of a surface the answer would produce. BlockLayout BlockKind = "layout" )
type Blocking ¶
type Blocking struct {
// Turn says the conversation's own turn is stopped on it.
Turn bool `json:"turn,omitempty"`
// Tasks names the work that is stopped on it, by title. It is empty when
// nothing is, and a surface draws nothing at all rather than "0 tasks".
Tasks []string `json:"tasks,omitempty"`
}
Blocking is WHAT IS PAUSED ON THIS QUESTION, which is a different fact from how urgent it is — and the one a person actually wants: a question nothing waits on is a question they may leave.
type Budget ¶
Budget is a ceiling and what has been spent against it.
TWO NUMBERS BECAUSE THERE ARE TWO WAYS TO RUN OUT, and a run bounded by one of them is bounded. Wall is how long the session may go on; USD is what it may spend. ZERO IS NO CEILING for each of them independently — an hours-only budget is a real thing to want — and a Budget with neither is not set at all (Budget.Set), which is the whole of how `--yolo` keeps its old behaviour.
THE SPENT FIGURES ARE READ, NEVER ACCUMULATED HERE. Wall comes off the clock and USD off the session's own journaled usage (rail.go reads the same figure), so this is a reading and not a second ledger to drift.
func (Budget) Exhausted ¶
Exhausted reports that the run is over on the budget, and says which ceiling in words a person reads. A budget nobody set is never exhausted.
THE WORDS ARE THE PERSON'S AND NOT THE MACHINERY'S: "the four hours are up", not "wall limit exceeded".
func (Budget) Left ¶
Left is what is still there to spend, floored at zero on each ceiling. A ceiling nobody set answers zero, which callers read together with Budget.Set rather than as "nothing left".
type CheckRun ¶
type CheckRun struct {
Command string
Passed bool
Tail string
// Unread says why this check was not started. NOBODY LOOKED IS A FACT, AND A
// FACT ABOUT THE RUN REACHES THE RECORD: it is neither a pass nor a failure,
// and an empty value means the command was read normally.
Unread string
// Failures carries stable identities read from a failed command's own
// output. Empty remains honest for a non-test validation or output the
// generic reader cannot parse: the command is red, but what failed is unknown.
Failures []string
// Ran says the command STARTED AND FINISHED — it was found, it executed, and
// the shell gave an exit status. A command that would not start and one the
// window cut off both answer false, and both are DIFFERENT NEWS from a check
// that ran and failed: a check nobody could run taught nobody anything about
// the tree, so a baseline may not record it as already-red (recording it
// there would silence a real failure on it later).
Ran bool
}
CheckRun is one of the session's declared checks, run and read.
Tail is the END of what it printed, for [checkpointResultTail]'s reason: what a check concluded is in its last lines.
type Completer ¶
type Completer interface {
CompleteWithMessages(ctx context.Context, messages []ai.Message, options ...ai.Option) (*ai.Response, error)
}
Completer is the narrow slice of provider.Client the loop needs. It is an interface so tests substitute a scripted completer.
func SeatCompleter ¶
SeatCompleter is next with every call marked as the seat's, so a guard attributes the call's spend to that seat even when another seat runs the same model or a fallback moves the seat to another. It keeps next's model chain, because a completer that dropped it would silently end model fallback for every task it wraps.
type Confidence ¶
type Confidence string
Confidence is how sure the asker is of its own pick, in three words a person would use. It is deliberately coarse: a percentage is a number nobody can check, and a person deciding whether to read further wants to know whether the asker is guessing, not how much.
const ( // ConfidenceSure is "I would do this". ConfidenceSure Confidence = "sure" // ConfidenceFairly is "I lean this way". ConfidenceFairly Confidence = "fairly" // ConfidenceUnsure is "I genuinely do not know", which is the one value // that says the question was worth asking. ConfidenceUnsure Confidence = "unsure" )
type Config ¶
type Config struct {
// WaitForBeltSteps is for the run worker that enforces its limits and
// delivers notes from tool-end events. Its sole event reader must close
// Event.BeltStepHandled after processing each such event. Other agents
// leave this off and their event streams remain asynchronous.
WaitForBeltSteps bool
Workspace string // tools root here; all relative paths resolve inside it
Model string
APIKey string
BaseURL string
Sources modelsource.Set
// AuthKeySource explains a refused worker account without supplying routing
// settings to a worker whose completer already owns its endpoint and key.
// Nil keeps ordinary sessions on their own profile and source ladder.
AuthKeySource func(model string) string
// System is the rendered system prompt. Empty renders the package's
// embedded default (prompts/system.md + the project footer) for
// Workspace and Model.
System string
// ContextWindow is the model's window in tokens; compaction fires at
// window − max(15% of window, 16384). Zero selects a conservative default.
//
// THE FIGURE IS THE MODEL CARD'S AND IT IS BELIEVED. What was once clamped
// to twice the default for every model alike is now capped only by what an
// endpoint has actually refused to serve ([TrustedWindowFor]), so a model
// with a million tokens of room is no longer folded like one with a hundred
// and twenty-eight thousand.
ContextWindow int
// ContextWindowFor answers from the catalog owned by the machine running
// the session. A model switch consults it there so a remote surface's
// different catalog cannot move this engine's compaction point.
ContextWindowFor func(model string) int
// Routing is how this session asks the router to choose among the endpoints
// serving its model, and whether it times them at all (internal/provider's
// velocity.go). EMPTY IS NOBODY HAVING CHOSEN: the session falls to the row
// this process installed and, with none installed, to the shipped row
// ([provider.DefaultRouting]), which sends no preference of ours at all.
//
// AND EMPTY IS WHAT A LAUNCH FROM A PROFILE LEAVES IT AT, on purpose: the
// profile's row is installed process-wide instead (internal/config's
// InstallLaneRows), so a person who cycles `routing` in the settings panel
// is answered by the very next request rather than by the next launch
// (issue #1022). The field is still the way a caller HANDS a row down — a
// child built from a parent's own config rather than from a profile — and
// such a caller still wins over the installed row.
Routing provider.RoutingStrategy
// CompactEnabled gates automatic compaction. Manual compaction via the
// surface's /compact is a surface concern and always available through
// Compact.
CompactEnabled bool
// SessionFile is the JSONL transcript: header line, then one line per
// journaled message and compaction marker. Empty keeps the conversation
// in memory only. If the file exists it is loaded on New and the
// conversation resumes after the latest compaction marker.
SessionFile string
// Place is the session folder and everything inside it (place.go,
// Decision 26). The zero Place is the legacy flat layout: sidecar paths
// keep deriving from SessionFile, droppings keep landing in the
// workspace's .codeaf, and nothing changes for a caller that has not
// adopted the folder. When set, SessionFile and Place.Transcript() name
// the same file.
Place Place
// ArtifactsIndex is the global deliverables index (artifacts.go): the file
// one row is appended to whenever this session produces something a person
// might want to find again — a generated picture, an exported conversation.
// Empty records nothing, which is what a test and a headless --once both
// want.
//
// It is the caller's path rather than one this package derives, for the
// reason SessionFile is: where a person's state lives is the surface's
// decision. The surface's answer is ~/.codeaf/v3/artifacts.jsonl, resolved
// through internal/home so CODEAF_HOME moves it with everything else.
ArtifactsIndex string
// Memory is the brain this session remembers into (memory.go): the store's
// event-sourced memories, routed into the prompt before a turn and written
// after one. NIL IS MEMORY OFF — no <memory> block, no reflex call, and no
// `remember` on the belt, so the model does not have the verb.
//
// It is the caller's store rather than one this package opens, for the
// reason SessionFile is a path rather than a directory: where a person's
// state lives is the surface's decision, and the door is also where the
// memory.enabled row is read. A door that turns memory off hands nothing
// here, which is what makes "no calls" structural.
Memory *store.Store
// Skills is the store the skill shelf is read from: the catalog section,
// the skills a message carries, and `use_skill`. NIL FALLS BACK TO
// Memory, so a door that names no shelf of its own reads the shelf in the
// store it remembers into, exactly as every door did before this field.
//
// IT IS A SEPARATE FIELD BECAUSE SKILLS ARE NOT MEMORY. A person who
// turned memory off asked for a conversation that carries nothing about
// them across conversations; they did not ask to lose the skills they
// installed for Claude Code or Codex, which live in folders on disk and
// say nothing about them. So a door with memory off hands no Memory — no
// block, no reflex call, no `remember` — and still hands a shelf here:
// one that holds only what the folders hold, built from those folders by
// the same import pass, and thrown away with the process (cmd/codeaf's
// v3SkillShelf). The folders stay the one source of truth either way.
Skills *store.Store
// ConversationHistory grants only indexed history reads. Workers inherit
// this interface without receiving memory extraction, writes, or journaling.
// Nil falls back to Memory, so a memory-off root grants no history access.
ConversationHistory ConversationHistoryReader
// MemoryImport is the legacy memory.md this session carries into the store
// on its first turn, once, before it is renamed to memory.md.imported
// (memory.go). Empty imports nothing, which is every caller but the v3 door
// and every machine that has already been through it.
MemoryImport string
// ApprovalPolicy decides whether a tool call runs, asks, or is refused
// (internal/approval, gated in consent.go). NIL ALLOWS EVERYTHING, which is
// the behavior every caller had before the gate existed: a headless --once
// and the tests run exactly as they did, and a surface opts into the policy
// by handing one over.
//
// It is the policy this session STARTS on and not the one it is stuck with:
// a surface that banks a rule mid-conversation replaces it with
// [Agent.SetApprovalPolicy] (approvalgate.go). This field itself is never
// written after New, which is what lets task_run.go copy the whole config
// without a lock.
ApprovalPolicy *approval.Policy
// ApprovalGate is the door onto the settings rows the gate is built from,
// so this conversation can move its own posture from inside itself
// (approvalposture.go). Nil is a session with no such dial — a test, a
// worker, a headless run — and the surface then draws no control for it.
ApprovalGate ApprovalGate
// ApprovalPosture is the posture the LAUNCH handed down — `--yolo` says
// [PostureAllow] here — for a conversation nobody has moved yet. It is in
// memory only and is never written to the folder, because a flag typed on
// a command line is a fact about this run; the moment a person moves the
// wheel the conversation's own word replaces it.
ApprovalPosture string
// AskConsent says somebody is watching this agent's events and will answer
// an EventConsentRequest with [Agent.ResolveConsent].
//
// It is the difference between a question and a hang. Left false — a
// headless caller, a cron run, --once — a policy's "prompt" decision denies
// the call with a result the model can act on, instead of blocking the turn
// on a question that will never reach a person.
AskConsent bool
// HarnessCards says a surface in THIS PROCESS holds the harness lane — the
// standing subscription every card raised on it is drawn from
// ([Agent.WatchHarnessDesigns]) — and will answer what arrives there.
//
// IT IS NOT AskConsent SAID TWICE, and the difference is a road rather than
// a mood. AskConsent is about the TURN'S OWN STREAM: an approval, a connect
// offer, a task proposal, all of which cross a connection as ordinary
// events. This lane is a standing subscription, and whether it reaches
// anybody is a question about the road: in one process it always does, and
// over a connection it does exactly when that wire carries the lane AND the
// answer the card asks for (internal/remote's standinglane.go, version 11 —
// before it, neither crossed and a card raised over a wire expired unseen).
// The one place that decides it for every door is cmd/codeaf's
// chatv3_lanes.go, which fills this field and HarnessStore together for the
// lane's two cards ([Agent.canProposeSubharness] and harness_build.go).
//
// LEFT FALSE IT TAKES THE VERB AWAY RATHER THAN BREAKING IT, which is this
// belt's law (tools.go): a model told it can offer a saved program plans
// around that ability for the rest of the conversation, long after the first
// offer nobody could answer.
HarnessCards bool
// Guardian turns on the small model that answers a "prompt" decision before
// the person is asked at all (guardian.go). FALSE IS THE DEFAULT AND THE
// ONLY SAFE ONE: this is a gate that answers on somebody's behalf, and a
// caller that has not said so must never get one. Nothing about the gate
// changes when it is off — not one extra call, not one extra branch a person
// can observe.
// TaskAudit gates the verified frontier (task_audit.go): when false, a
// finished node merges on its own report — faster and cheaper, and
// 'done' stops meaning 'proven'. The config row (task.audit) defaults on.
TaskAudit bool
Guardian bool
// AttributionModelOff is the person's `attribution.model` row turned off
// (internal/config's KeyAttributionModel, env CODEAF_ATTRIBUTION_MODEL):
// the `Assisted-by` line in the commits codeaf signs is then the bare
// `Assisted-by: CodeAF`, with no model named. It reaches both readers there
// are — the belt fact the model is told (beltfacts.go's
// [Config.assistedByModel]) and the mechanical commit a landing writes
// without asking anybody ([Agent.signsGitWork]).
//
// IT NEVER TURNS SIGNING OFF. The `attribution` row that did is gone
// (2026-09-23): codeaf signs every commit, pull request and issue it writes.
//
// IT IS A RESOLVED BOOL AND NOT A PROFILE PATH, for the reason [TaskAudit]
// beside it is: a task node is handed no ProfileDir at all, so the row is
// resolved once at the door and travels down with the work. It is spelled
// as the OFF so that its zero value is the product default, and a caller
// that said nothing names the model.
AttributionModelOff bool
// ReplyGuardOff turns off the watch on replies that stop being language
// (internal/provider's streamguard.go). The config row (reply.guard)
// defaults ON, and this field is spelled as the OFF state so that a Config
// nobody filled in keeps the guard rather than silently losing it.
//
// It says nothing about the silence watchdog beside it, which has no switch.
ReplyGuardOff bool
// TaskSettle is who decides a task that landed needing a look — the
// `task.settle` row, as the person set it ([TaskSettle]). Empty is
// [TaskSettleAsk], which is the default and the only value a caller that has
// said nothing may get: a session must not start settling work on somebody's
// behalf because a field was left blank.
//
// It changes exactly one string — the landing note a settled node writes to
// whoever asked for the work (task_run.go's [taskNote]). Nothing about the
// three answers changes: the tool takes the same verbs and the surface offers
// the same choices whichever way this is set.
TaskSettle string
// Standing is the ambient side (standing_contract.go, internal/standing).
// Nil is off: no belt tool, no card, no ticking from this process.
Standing *Standing
// ProfileDir is the person's profile directory — the one holding the
// config.json that /settings writes (internal/config's settings registry).
// It is what the settings and change_setting tools are a door onto
// (tools_settings.go): the model can read the person's settings back and
// change one permanently, by the row's own registry key and through the
// row's own validated write.
//
// EMPTY KEEPS BOTH TOOLS OFF THE BELT, on the absence law every conditional
// family here states: a settings tool with no profile behind it would answer
// every call with the same refusal, and a model told it can change a setting
// will plan a whole reply around one. A headless --once, a task node and
// every test get exactly what they had before this field existed.
//
// It is the caller's path rather than one this package derives, for the
// reason SessionFile is: where a person's state lives is the surface's
// decision, and a package that resolved ~/.codeaf itself would write there
// from a test.
ProfileDir string
// RolesSource reads one auxiliary-model setting for internal/roles: the
// keys are roles.PinKey and roles.TierKey. Nil is a fresh install with no
// settings file, and every auxiliary call then rides the session's own
// model — roles.Resolve's floor, not a failure.
RolesSource func(key string) (string, bool)
// RouteCrew picks one task's crew — worker, planner and checker — for the
// task in the ask (internal/config's RouteCrew over this profile, wired by
// the surface). NIL IS NO ROUTER: the run's seats are then the role
// ladder's, as they were before crews were routed, and no crew row is
// logged. It is never a model this package chooses (taskcrew.go).
RouteCrew func(config.CrewAsk) (crewroute.Decision, error)
// SupportsImages reports whether a model can read image content parts. It
// gates [Agent.SubmitImage] and NIL IS FALSE — the opposite of every other
// nil-is-permissive hook here, and deliberately so: a model that cannot see
// answers a message full of image parts with a 400 or, worse, with a
// confident description of nothing. "I don't know whether this model has
// vision" and "this model has vision" must not be spelled the same way, so a
// caller that holds no catalog gets a refusal it can read instead of a turn
// that fails on the wire.
//
// It is a function of the model rather than a bool because the model moves:
// /model swaps it mid-session (see [Agent.SetModel]), and the answer has to
// follow the model the next turn will actually ride.
SupportsImages func(model string) bool
// ReasoningProfile is the row's account of a model's thinking pass —
// whether it can be turned off, which effort words it takes — under the
// same never-blocks contract as SupportsParameter, and nil is the same
// "nobody knows". The adapter reads it to send a model that cannot stop
// thinking its lowest level instead of a disable it would refuse.
ReasoningProfile func(model string) (provider.ReasoningProfile, bool)
// SupportsParameter answers whether a model accepts a request field, and
// whether anybody knows (internal/catalog's SupportsParameter states the two
// bools). The adapter asks it before it lets an optional knob travel, so a
// reasoning level set on a model that publishes no reasoning parameter is
// simply not sent instead of narrowing the endpoint set to nothing.
//
// NIL IS "NOBODY KNOWS", which is not the same as "no": an unwired seam
// leaves the adapter's own explicit-only rule in force, which is exactly the
// behaviour every caller had before this field existed.
SupportsParameter func(model, parameter string) (bool, bool)
// ModelPrice is a model's own published list price, per token in US dollars,
// and whether anybody published one (internal/catalog's PriceNow). The
// adapter bounds a latency-sorted request against it, so a session whose
// routing row asks for speed is not also charging several times what the
// model itself costs.
//
// NIL IS "NO PRICE IS KNOWN", which sends no ceiling and routes exactly as an
// unwired session always did.
ModelPrice func(model string) (prompt, completion float64, known bool)
// TaskProgressCheck is the test seam for leash checkpoints. Production uses
// the node's ordinary read-only checker; a test may answer deterministically.
TaskProgressCheck func(brief string, evidence []string) (working bool, reason string)
// TaskLanded is called once per landed node, on its own goroutine, after the
// node's row is in the project's index (task_run.go's [Agent.reportTaskNode]).
// It carries [TaskLanding]: the node's record as the landing left it, the
// worker's model and the model the checking pass ran on (empty when there was
// none). A final state only — running and queued nodes land nothing — and a
// call that never blocks the reporting path: the reporting goroutine hands the
// landing over and moves on, and a caller that is slow holds up nothing but
// its own goroutine. Nil is off, which is what every caller that does not
// want the news hands in, and what this package then spends nothing on.
TaskLanded func(TaskLanding)
// TaskDeadline overrides one checkpoint interval. Zero keeps the one-hour
// production interval and lets deadline behavior be tested without an hour.
TaskDeadline time.Duration
// HarnessDesignWindow overrides how long a sub-harness design is given to
// WRITE ITS PAGE (harness_build.go's harnessDesignWindow). Zero keeps the
// half-hour production window. It bounds the writing only — the card that
// follows waits on the person for as long as they take — and it is settable
// for TaskDeadline's reason: what happens at the end of the window is worth a
// test, and half an hour is not a thing a test can wait for.
HarnessDesignWindow time.Duration
// ModelFallbacks are the models a turn moves to, in order, when no endpoint
// serving this session's model will accept the request's shape at all
// (internal/provider's endpoints.go). It is the person's own models.fallbacks
// row; empty means the catalog is asked for the nearest same-class model
// instead, through NearestModels.
ModelFallbacks []string
// NearestModels names the models closest to one that just refused
// everything. It is consulted ONLY when ModelFallbacks is empty, and it never
// waits: a catalog that has not resolved answers nil, and a chain with no
// fallback simply ends in the diagnosis instead of on another model.
NearestModels func(model string) []string
// Harnesses is this build's sub-harness registry, in the fields a turn is
// matched against: name, description, and the cue list the designer froze at
// build time (internal/subharness). EMPTY IS DETECTION OFF, which is every
// caller that has not loaded a registry, and it is off at the cost of one
// length check per turn.
//
// The whole registry is handed over rather than a path to it for the reason
// SessionFile is a path and not a directory this package picks: where the
// entries come from is the surface's business, and a package that read
// ~/.codeaf/harnesses itself would read it from a test and from a task
// node's own agent too.
Harnesses []subharness.Entry
// RunHarness runs one harness for one turn and returns its report. The name
// is an entry's own Name; the text is the person's words, verbatim — less
// the clause that chose the model, when they wrote one.
//
// The model is what the turn asked the run to ride, resolved against
// TaskModels (harness.go). EMPTY IS THE ORDINARY CASE and means nobody
// said: the runner uses whatever model it was built on, which is what every
// run did before a turn could name one.
//
// NIL IS DETECTION OFF, whatever Harnesses holds, and it is the seam that
// keeps the engine out of this package: the conversation decides WHETHER a
// harness runs — it is the half a person answers — and the engine decides
// what running one means.
//
// step is where the engine reports each step as it lands, and it is what
// makes a run something a person can WATCH rather than wait out: the report
// only exists when the whole thing is over. It is never nil, so a runner
// calls it without checking; a runner with nothing to report simply never
// does. Calling it BLOCKS the run for as long as the send takes, which is
// why what is behind it is one hub send and nothing else.
//
// The [subharness.Usage] is WHAT THE RUN COST, summed over every model call
// it made, and it is returned rather than left to the engine because the
// person paying for it is sitting in this conversation: a run bills through
// the auxiliary door and lands in /cost, on the status line and against the
// spend rail (harness.go). A runner that cannot account for its calls
// returns the zero value, which is a run this session does not claim was
// free — it is a run nobody reported a price for, and the emptiness law
// says to show nothing rather than a zero.
RunHarness func(ctx context.Context, name, text, model string, step func(subharness.Trail)) (string, subharness.Usage, error)
// HarnessStore is where a harness this conversation DESIGNS is written, and
// it is the same registry Harnesses was read out of (harness_build.go). The
// model's build_harness hand reaches the designer through it (tools_harness.go);
// a page nobody approved never touches it.
//
// NIL IS BUILDING OFF, on exactly the terms RunHarness is detection off — and
// the two are checked together, because a harness this session can write and
// cannot run would be a page saved into a registry with no engine under it.
//
// It is the STORE and not a path for the reason Harnesses is a slice: where
// the registry lives is the surface's decision, and a package that opened
// ~/.codeaf/harnesses itself would open it from a test and from a task node's
// own agent too.
HarnessStore *subharness.Store
// OrchestrateRunner launches one adaptive run (internal/orchestrate): the
// goal, the model the turn named (empty is the session's), and the fuel
// cap in dollars. It returns the run's id; events stream on the standing
// lanes as EventOrchestrateNote/Fuel/Pause.
//
// NIL IS ORCHESTRATION OFF, the same posture RunHarness keeps: a surface
// that was not handed a runner never offers an adaptive run, at the cost
// of one nil check per turn.
OrchestrateRunner func(ctx context.Context, goal, model string, capDollars float64) (string, error)
// Subharnesses is this surface's subharness registry: the compiled-in Go
// programs it built, and the stores it put in front of them
// (docs/SUBHARNESS-CONTRACT.md). It is the registry itself rather than a
// path for the same reason HarnessStore is a store — where the bundles live
// is the surface's decision, and a package that opened
// ~/.codeaf/subharnesses itself would open it from a test and from a task
// node's own agent too.
//
// NIL IS SUBHARNESSES OFF, on exactly the terms RunHarness is detection off.
// The three doors in subharness_contract.go answer nothing, calmly, and a
// surface built against them draws nothing rather than an error — which is
// the "absent, not broken" law arriving at a door that was never wired.
Subharnesses *exec.Registry
// Delegates is the programs this build carries that a task can be handed to
// whole — senior-dev first (delegate_door.go, internal/delegate). The
// surface hands in the build's list (internal/delegate/builtin) rather than
// this package importing it, so a test of this package never carries a
// program's whole engine. EMPTY IS NONE: the door lists nothing, `via`
// refuses every name, and the prompt says nothing about them.
Delegates []delegate.Delegate
// SubharnessMemory is where a running subharness keeps what it has learned
// about its OWN domain — its file in its own bundle, never this
// conversation's memory (subharness_env.go's [SubharnessMemory] says why the
// two must not share a page).
//
// NIL IS A BUILD WITH NO BUNDLE MEMORY, and the remember/recall doors then
// answer [exec.NotWired] for their own names, which is the contract's own
// answer for a door with nothing behind it. It is a SEAM the store lane
// fills, on the terms Subharnesses is one: where a bundle's memory lives is
// the surface's decision, and a package that opened
// ~/.codeaf/subharnesses itself would open it from a test too.
SubharnessMemory SubharnessMemory
// SubharnessLastRun is the dim note under one row of the `/subharness` list:
// when that program last ran here and how it went, in a person's words
// ([SubharnessRow.LastRun]). It is a closure rather than a table because the
// answer is about the moment the list is drawn, and a snapshot taken at
// launch would be silent about the run that finished five minutes ago.
//
// NIL IS NO HISTORY, and every row then draws nothing there — never "0 runs",
// never "never run" (the emptiness law). It is the STORE LANE's seam: the run
// journals it keeps beside each bundle are the only thing that can answer.
SubharnessLastRun func(name string) string
// SubharnessRecordRun is told how one run went, the moment it lands
// (subharness_run.go). It is the write half of [Config.SubharnessLastRun] and
// it is the STORE LANE's seam too — the note goes beside the bundle, which is
// the only place a later session can read it back from.
//
// NIL IS A BUILD THAT KEEPS NO HISTORY, and a run then simply leaves none. It
// is not an error and nothing is drawn about it: a list with no notes is what
// a machine that has run nothing looks like, and the two are the same picture
// on purpose.
SubharnessRecordRun func(name string, note SubharnessRunNote)
// WorktreeRoot is where isolated worktrees for a run's write-capable
// nodes live. The session-id wave owns what fills it; this is the
// ABSTRACT SEAM — a path per job id, nothing more. EMPTY means worktree
// nodes share the workspace instead, which is the safe degradation.
WorktreeRoot string
// Media and MediaModel are the v3-revision media pair (docs/MULTIMODAL.md
// Decisions 5-8): the one client that reaches every generation endpoint —
// /images, /audio/speech, /videos — and the ONE USE-TIME RESOLVER that
// answers which model serves a modality. MediaModel takes exactly one of
// "image", "speech", "video", "vision" and answers a slug the resolver has
// already capability-checked against the catalog, or "" when that modality
// has no capable model; the ladder behind it (settings slot → role pin →
// best catalog candidate → curated fallback) is the surface's business,
// which is why this is a closure and not a table.
//
// The absence law is per-verb: a nil Media keeps every generation tool off
// the belt; a nil MediaModel (or one answering "") keeps that MODALITY's
// tools off ([Agent.mediaHand]). They REPLACED a pre-revision pair of this
// config's own — an image client and an image slug, with a pin ladder the
// tool walked itself — and nothing of that pair survives: one client and one
// resolver serve every verb, so a machine cannot paint and be unable to
// speak for reasons nobody can find.
Media MediaGenerator
MediaModel func(modality string) string
// MediaPick is the just-in-time half of the pair above: where MediaModel
// answers "the default for this modality", MediaPick answers "the model
// asked for THIS name, for this one call". It takes the same modality word
// and the model's own word for what it wants — a slug, a fragment like
// "seedream", or "best" — and answers the resolved slug, or an error in
// words the model can act on ("no image model matches", "X makes speech,
// not image"). An empty word answers ("", nil), which the belt reads as
// "keep the default".
//
// NIL MEANS THE CHOICE DOES NOT EXIST: the making verbs advertise no
// `model` argument at all, by the same absence law as the verbs themselves
// — a knob with nothing behind it is left off the schema rather than
// present and refused. The surface that wires it (cmd/codeaf's
// chatv3_media.go) answers from the same catalog the defaults ladder
// reads, so a picked model is capability-checked exactly as a default is.
MediaPick func(modality, word string) (string, error)
// DocumentEngine is the rung read_document climbs to (tools_doc.go): the
// person's document_engine row, one of auto, local, free or ocr
// (config.DocumentEngines), resolved by the surface exactly as the search
// pair below is and handed over as the answer.
//
// EMPTY IS AUTO, not "off". Unlike the two pairs around it, this is a
// preference and not a back end: the rungs ride this session's own API key
// and base URL, so there is nothing a nil here could mean except "nobody
// chose", and config.DefaultDocumentEngine is what nobody-chose resolves to
// everywhere else in the binary. The tool is on the belt either way, because
// read's own scanned-PDF refusal names it by name and a named way out that
// resolves to nothing is worse than a rung that says why it cannot run.
DocumentEngine string
// SearchProvider and SearchFetcher are the web-search pair the belt's
// web_search and web_fetch tools call through (tools_search.go). They are
// [search.Provider] and [search.Fetcher] rather than a configuration because
// WHICH back end answers is not this package's question: internal/search
// owns the resolution law, and the surface hands over live wrappers that run
// it against the person's current settings for every operation.
//
// NIL IS THE DEFAULT AND MEANS THE TOOL IS NOT ON THE BELT — not that it
// is on the belt and fails. A model told about a tool it cannot reach is
// strictly worse off than a model never told: it will spend a call, read a
// refusal, and often try again in different words, and the whole time it
// is planning around a capability that does not exist. The two are
// separate fields for the same reason [search.Resolve] returns two: a
// binary that can search but not fetch is a real configuration, and it
// should get exactly the one tool it can honour.
SearchProvider search.Provider
SearchFetcher search.Fetcher
// Connect is the person's connected accounts (internal/connect): which
// services this build can offer, which of them are connected on this
// machine, and an authorized client for each one that is.
//
// NIL IS THE DEFAULT AND MEANS THE FEATURE IS ABSENT — no services tool, no
// use_service, and nothing on the belt that mentions an account. It is the
// same law the search pair above states and it is stated again because the
// cost of breaking it is larger here: a model told it can read a mailbox
// will plan a whole answer around one, and a refusal at the end of that plan
// is a turn spent on a capability that never existed. A build with no
// registration for any service hands over nil and the conversation is exactly
// what it was before this field.
Connect *connect.Manager
// Effort is the rung this session was HANDED — the work's own rung, filling
// the ladder's task scope. It is set on a child: a task worker gets the
// task's rung, a standing firing gets the item's. EMPTY IS THE HONEST
// DEFAULT and means nobody set one for this piece of work, which is every
// conversation a person opens themselves.
Effort effort.Rung
// EffortRole is what this session is FOR, and it is the rung of last resort
// before the install's default: a standing firing and its checks stay cheap
// however deep the install is dialled, and an errand asks for nothing at
// all. THE ZERO VALUE IS NOT A ROLE and falls through to DefaultEffort,
// which is the right answer for a caller that has not thought about it — a
// headless --once, a test — because it is the same answer a person's own
// conversation gets.
EffortRole effort.Role
// DefaultEffort is the install's `effort` row, read by the door
// (config.DefaultEffortAt). EMPTY ASKS FOR NOTHING, which is what a session
// built without a door has always sent: config.Ship is the shipped answer to
// the settings row and never a default this package invents, so a caller
// that wires no profile is not silently opted into paying for depth.
DefaultEffort effort.Rung
// TaskModel is the model a task runs on when its proposal names none — the
// person's task.model row. EMPTY IS THE CONVERSATION'S OWN MODEL, which is
// the behaviour every task had before this field existed: a node is the same
// worker doing the same job somewhere quieter, so the same model is the
// honest default. It is resolved through the same matcher a proposal's word
// is (taskmodel.go), so a row written "opus-5" reaches the same id.
TaskModel string
// TaskModels lists the models a task may be sent to — the surface's catalog,
// as ids. It is the seam a `model` argument is validated and resolved
// against, and it is a function for the reason SupportsImages is one: the
// list arrives from a lazily loaded catalog and is not the same list at boot
// as it is a minute later.
//
// NIL IS "NOBODY CAN SAY", not "there are none". A caller that hands over no
// list gets every named model taken as written and the provider's own error
// if it is wrong — exactly what every caller had before the argument existed
// — because a package with no catalog refusing a model id would be inventing
// a catalog to refuse from.
TaskModels func() []string
// TaskAutoApproveSeconds is how long a task proposal waits before the clock
// approves it (task.go, config.KeyTaskAutoApprove). 0 IS A CLOCK THAT IS
// OFF — the proposal waits for [Agent.ResolveTask] and nothing else — which
// is only a sentence a WATCHED session can honour: with nobody subscribed
// to the events (AskConsent false, or no turn hub), the deadline approves
// whatever this says, because a headless run has no one to wait for.
//
// It is seconds rather than a Duration because it is one settings row read
// straight off the sheet, and a surface counting it down draws the same
// number the person typed.
TaskAutoApproveSeconds int
// BashBackgroundAfterSeconds is how long a foreground command stays in the
// turn before the same running process is kept as a job (promote.go,
// config.KeyBashBackgroundAfter). 0 TURNS THE CLOCK OFF, preserving the
// timeout-only posture for tests and callers that do not use the v3 door.
BashBackgroundAfterSeconds int
// TaskRepairRounds is how many times a node whose work came back with gaps
// is handed back to a fresh worker in the SAME worktree before it lands as
// incomplete (task_audit.go, config.KeyTaskRepairRounds). 0 IS THE LOOP
// TURNED OFF: the first gap ends the node, which is how the frontier worked
// before the loop existed.
//
// Zero is also the zero value, and that is deliberate rather than a defect —
// it is [TaskAutoApproveSeconds]'s arrangement, for the same reason. A caller
// that builds a Config and says nothing about repair gets the behaviour that
// spends nothing extra, and the DEFAULT of one round is the door's answer
// (config.DefaultTaskRepairRounds), read from the person's own settings.
TaskRepairRounds int
// TaskParallel is how many task nodes may RUN AT ONCE, and 0 IS NO LIMIT
// (task_run.go's frontier, config.KeyTaskParallel). It is the person's own
// number and it is off by default, because the count of nodes was never
// what runs out: what runs out is this machine's cores and memory — see
// TaskMaxLoad and TaskMinFreeMB below — and the provider's rate limit,
// which the adapter already adapts to on its own.
//
// Zero being both "no limit" and the zero value is deliberate, in
// [TaskRepairRounds]'s arrangement: a caller that builds a Config and says
// nothing about parallelism gets the ceilings that are really there rather
// than a number this package invented for it.
TaskParallel int
// TaskMaxLoad is the one-minute load average PER CORE at or above which the
// frontier stops starting new nodes (task_pressure.go,
// config.KeyTaskMaxLoad). 0 turns the load check off.
//
// Per core rather than raw, because the same reading means opposite things
// on a two-core laptop and a thirty-two-core workstation, and a person's
// setting has to mean one thing on both.
TaskMaxLoad float64
// TaskMinFreeMB is the floor of AVAILABLE memory — the kernel's
// MemAvailable, what a new process could actually get — below which the
// frontier stops starting new nodes (task_pressure.go,
// config.KeyTaskMinFreeMB). 0 turns the memory check off.
//
// Both of these gate ADMISSION and nothing else. A node that is already
// running keeps its worktree and its child agent however loaded the machine
// gets, which is what lets pressure drain instead of having to be relieved.
TaskMinFreeMB int
// TaskLanes is THE ACCOUNT OF THIS MACHINE'S RUNNING TASK LANES, shared by
// every conversation this process opens ([NewTaskLanes]). The memory half
// of the reading above is `treeResidentMB(os.Getpid())` — this process and
// every descendant it started — and /proc cannot say which conversation
// started which compiler, so the count that reading is divided by has to
// cover the same work: every lane the process is running, not one graph's
// (task_pressure.go's ONE ACCOUNT FOR THE WHOLE PROCESS, #907).
//
// The process's own door sets it once and hands the same pointer to every
// conversation (cmd/codeaf). Left nil, a graph is ALONE IN ITS PROCESS and
// keeps an account of its own — which is the truth for an embedder with one
// conversation, and for every scripted graph in the tests.
TaskLanes *TaskLanes
// InTask marks this agent as ONE TASK NODE'S RUNNER (task_run.go) rather
// than the conversation. It changes exactly two things, and both are
// consequences of the same fact — there is nobody to talk to:
//
// - the belt leaves off propose_task and watch (tools.go): a node does
// the work it was briefed with, and a watch's news has no conversation
// to arrive in.
// - a call the policy would ask about is REFUSED in the node's own words
// (consent.go) instead of hanging or borrowing the session's wording
// about a resolver that was never going to be attached.
//
// It is false for every conversation, and no surface sets it: the executor
// sets it on the config it builds for a node and nowhere else.
InTask bool
// Errand marks this agent as the short exchange behind home's `ask here`
// (cmd/codeaf's chatv3_exchange.go) rather than a conversation somebody
// sits in. It is a conversation in every other way — a real model, a real
// transcript, a card it can answer — so InTask would be a lie about it.
//
// IT CHANGES EXACTLY ONE THING: an errand is never registered as a live
// delivery target (standing_run.go). A firing steered into an exchange is
// news typed into a forty-cell pane that closes with home, and the person
// sitting in an ordinary conversation in the same window is never told —
// which is what happened the first time a reminder made from home ever
// fired.
//
// Ratifying the exchange's OWN card is untouched by this, and the two are
// separate lanes on purpose: a card is answered through the agent the
// surface is holding ([Agent.ResolveStanding]), never through the registry,
// so an exchange still proposes and still hears yes.
Errand bool
// Divide arms the division road for the tasks this session admits
// (task_divide.go). ON is what the v3 door wires (cmd/codeaf's chatv3.go,
// from internal/config's Swarm, default true); the zero value is off, which
// is what keeps every scripted agent in this package's tests exactly as it
// was.
//
// IT IS THE ROAD AND NOT THE DECISION. A task is armed one at a time and
// only when something says its work might be wide ([Agent.armDivision]), and
// a worker that IS armed still has to get a division past the evidence and
// the free hands before anything is born. This row only says the road
// exists.
Divide bool
// SpendRailUSD stops a session that has spent this much. 0 is off. The
// check happens BEFORE a turn starts (rail.go) and reads the session's own
// journaled usage, so the rail is exact rather than an estimate, and a turn
// already in flight is never cut in half by it.
SpendRailUSD float64
// Unattended retains the legacy opt-in to automatic goal continuation.
// Together with Budget it selects a Steward only when Interactive is false.
// Tool approval policy is configured separately by the launch door.
Unattended bool
// Interactive says A PERSON IS STEERING THIS CONVERSATION — the door's own
// fact, and the one thing `--yolo` is not: yolo is approvals, this is who
// is watching. [newPrincipalFor] answers a [Person] for it even under a
// budget, so the person's latest words are the ask and work they handed to
// a task keeps its own assignment. --once and every worker door leave it
// unset, keeping the unattended [Steward] exactly as it was.
Interactive bool
// OneModel is the door's `--one-model` promise kept where it can actually be
// kept: EVERY TEXT CALL THIS SESSION MAKES RIDES THE CONVERSATION'S MODEL,
// including the roles that otherwise refuse to fall back to it.
//
// It has to be carried as a bit rather than expressed as an empty ladder
// because two callers hold the opposite law on purpose. The mark's reader and
// the brief's writer are CREW-ONLY — they hand [Agent.callRole] an empty
// floor so an install with no mastermind gets no second opinion at all rather
// than the running model marking its own work (checkpoint.go's two
// [roles.Register] calls) — and under the flag that left them with no pin, no
// tier and no floor, which is a role with no model rather than a role on the
// session's. A person who passed the flag has said the conversation's model
// IS the crew, so the answer is given once at the seam and the next crew-only
// caller is right without knowing the flag exists (#443).
//
// The media slots are untouched by it, for the door's own reason: vision,
// image, speech and video are capability-qualified, and a text model settled
// on them would not be one model, it would be a broken one.
OneModel bool
// Budget bounds automatic continuation in a fixed headless run. For an
// interactive conversation it bounds new turn admission independently of
// who owns the goal; already running work may finish beyond the limit.
// SpendRailUSD remains a separate, adjustable conversation spending limit.
Budget Budget
// PromptProfile is the person's own answer to which prefix this session
// sends, in the three words the settings row takes: `auto`, `lean`, `full`
// (internal/config's [config.PromptProfileModes]). It is what the door read
// off the sheet, not what was settled from it.
//
// `auto` and the empty string are the same answer — WORK IT OUT — which is
// what every door that has not been taught this row hands over and what
// every session did before the row existed. The word `lean` or `full` is the
// person overruling the window, and it loses only to the environment pin
// (promptprofile.go's [resolvePromptProfile] is the whole ladder).
PromptProfile string
// contains filtered or unexported fields
}
func (Config) WorktreePath ¶
WorktreePath resolves where job id's isolated worktree would live. Empty root means empty path, and an empty path means the node shares the workspace — the planner's worktree flag degrades, it never errors.
type ConsentScope ¶
type ConsentScope string
ConsentScope says how long one answer lasts.
const ( // ConsentOnce answers this call and nothing else. ConsentOnce ConsentScope = "once" // ConsentToolSession answers every later prompt for the SAME TOOL, for the // rest of this agent's life. // // It is deliberately coarse — "bash" means every bash command the policy // would have asked about, not the one that was asked about — because the // alternative is a per-argument memo the person cannot hold in their head: // they would be agreeing to a set they have not seen. A tool whose calls // deserve individual answers should say so in the policy, where a rule can // name the pattern; that is what the bash pattern list is for. ConsentToolSession ConsentScope = "tool-session" // ConsentRule answers this call and says a RULE HAS BEEN WRITTEN that covers // it — the surface banked the shape the person picked into their own settings // before sending this (internal/config's approvalmemory.go). // // It exists because the memo above is keyed by TOOL NAME ALONE, and for bash // that is wider than anything the card ever promised: a card that said // "always, this command" and left behind a memo meaning "every bash command" // was a card that lied by one word, in the direction that matters. So a // surface that wrote a real rule says so with this scope, and the gate writes // NO memo — the rule is what answers the next call, and it answers only the // calls it matches. // // The honest consequence, stated here because it is surprising: this scope // makes the next call go back through the policy. A shape narrower than the // person expected means being asked again, which is the card's promise kept // rather than broken. ConsentRule ConsentScope = "rule" )
func ConsentScopeOf ¶
func ConsentScopeOf(action AnswerAction, answer Answer) ConsentScope
ConsentScopeOf is how far one consent answer actually reaches.
It is the key's own scope (AnswerFromKey) in every case but one: a widening yes whose rule the surface has already written down is a ConsentRule, and AnswerBanked is where that fact rides.
It is exported for the same reason AnswerFromKey is: anything that applies an answer to this lane without going through Agent.ResolveQuestion — a stand-in, a link that resolves on the far side — has to reach the one mapping rather than write a second.
type ConversationHistoryReader ¶
type ConversationHistoryReader interface {
FindConversationMessages(context.Context, string, string, string, int) ([]store.MessageHit, error)
ConversationExchange(context.Context, string, int64, int, int) ([]store.MessageHit, error)
Session(string) (store.Session, bool, error)
}
ConversationHistoryReader is the read-only authority carried down a task family. Keeping mutations out of this interface prevents search access from silently enabling remember or posting worker traffic into the person's chats.
type DaySpend ¶
type DaySpend struct {
// At is the bucket's first local moment — its identity, and what a page sorts
// or seeks on.
At time.Time
// Label is the bucket said out loud, in [UsageWindow.Label]'s spelling:
// "aug 12". A week's label is the Monday it starts on; a month's is its
// first.
Label string
Calls int
// Tokens is input plus output as one sum, which is the figure every surface
// in this codebase draws ([TaskIndexEntry.Tokens] states the law).
Tokens int
USD float64
}
DaySpend is one bar of the sparkline: a bucket, and what was spent in it.
It is called a DAY because that is what it is at the grain every window starts on and the word a person uses for a bar; at week or month grain the same struct is a week or a month and DaySpend.Label says which.
func UsageByDay ¶
func UsageByDay(lines []UsageLine, window UsageWindow) []DaySpend
UsageByDay buckets lines into the window's own buckets, in order, WITH THE EMPTY ONES PRESENT.
The zero buckets are the point. A fortnight with four quiet days in it is a fortnight, and a series that dropped them would draw ten bars where fourteen belong — every quiet stretch compressed away and every busy one made to look continuous. So a bucket nothing landed in is a bucket with zero in it, and what a PAGE does with that zero is the page's business (this file's header says why the emptiness law splits here).
Lines outside the window are ignored rather than clamped into its ends: a window is a question about a stretch of time, and money from outside it piled onto the first bar would be an answer to a different one.
── WHICH DAY A ROW BELONGS TO ──
THE WRITER'S DAY, ALWAYS — UsageLine.Day, the local calendar day the process that made the call was standing in. It is written into the row for exactly this reason ([usageDayLayout] says so): a machine in Toronto records a call at 23:30 as August 25, and a reader that re-derived the day from the timestamp would charge it to August 26 the moment anybody read the ledger under a different TZ — over ssh, in a container, in a test. A day that moves depending on who is asking is not a day.
WEEKS AND MONTHS BUCKET BY THAT SAME DAY, parsed as a LOCAL date and taken to the Monday or the first of the month around it. Said plainly, because it is a real consequence rather than a detail: a viewer in another zone sees the WRITER'S days, grouped by the READER'S calendar — which is right, since the only thing a week or a month can be here is a set of whole days, and the days were settled where the money was spent.
A row with no Day on it — an older ledger, a hand-written line — falls back to its timestamp read locally, which is the best that can be said about it.
func UsageTotals ¶
UsageTotals is what a whole stretch came to: the header line's three figures. It is a function rather than a sum a page writes itself so that the page and the bars can never disagree about what "last 14 days · $34.10 · 41.2M tokens" is a total of.
type DecidedBy ¶
type DecidedBy string
DecidedBy is WHO answered, and it is the field that makes a decision record worth keeping: a person reading the record months later wants to know whether they said this or whether something said it for them.
const ( // DecidedByPerson is somebody pressing a key. It is the ordinary answer. DecidedByPerson DecidedBy = "person" // DecidedByDial is a policy taking the asker's own pick because nobody was // there. DecidedByDial DecidedBy = "dial" // DecidedByRecord is an earlier decision answering this one. DecidedByRecord DecidedBy = "record" // DecidedByAsker is the asker answering itself, which happens on the // ratify rung: the work was already done and nobody objected. DecidedByAsker DecidedBy = "asker" // DecidedByWindow is ANOTHER WINDOW ON THIS CONVERSATION. It is stamped by // the surface that LEARNS of an answer rather than by the one that gave it // — the giver knows perfectly well it was a person, and the value is there // so the second window's receipt does not say `you` about a key somebody // pressed on a different screen (docs/design/questions/DESIGN.md's FIRST // ANSWER WINS). DecidedByWindow DecidedBy = "window" )
type Decision ¶
type Decision struct {
Verb DecisionVerb
Brief string
Reason string
Observed []string
// Spent marks the ONE stop that is about money and hours rather than about
// the work: the budget is gone. It is read where a stop may have to end a
// turn over work that is still moving ([Agent.endTurnUnderSteward]), which
// only this stop may do — SPENDING IS THE THING A BUDGET FORBIDS, and
// waiting for the moving work to come home is more of exactly what ran out.
// Every other stop is about the work and can afford to let the work finish.
Spent bool
}
Decision is what a principal answered.
Observed is WHAT THE ANSWER WAS TAKEN ON, in the same words a person reads — the items a reading actually showed. It rides beside the brief because the brief is addressed to the MODEL and is written to be worked from, while the one line a person is shown when carrying on stops ([checkpointCarriedOnNote]) has to say what was seen and nothing else. A note that had only the brief to go on asserted "it is still not finished" as a fact, which is a claim nobody took a reading of (#468).
type DecisionClause ¶
type DecisionClause struct {
// Text is the segment as it reads, with no separator on either end.
Text string
// GiveUp is the order a row too narrow for the whole line surrenders its
// clauses in — the HIGHEST number goes first, and zero is never given up.
//
// IT IS HERE RATHER THAN IN THE SURFACE THAT DOES THE GIVING UP, because a
// clause and what it is worth are one fact about the record. A surface that
// ranked them itself would be a second opinion about which half of a
// decision matters, kept in a file that never sees the other half.
GiveUp int
}
DecisionClause is one segment of DecisionRecord.Line, with what it is worth beside it.
type DecisionRecord ¶
type DecisionRecord struct {
// ID and Ref name the question that was answered, exactly as [Question] did.
ID uint64 `json:"id"`
Ref string `json:"ref,omitempty"`
// Kind is the lane and Ask is the shape, both as the question carried them.
Kind QuestionKind `json:"kind,omitempty"`
Ask AskKind `json:"ask,omitempty"`
// Head is the question's own sentence, kept verbatim, because it is what a
// later question is matched against and what a person reads in the record.
Head string `json:"head"`
// Subject is what it was about, and it is part of the match: the same
// question about two files is two decisions ([decidedAlready] says why).
Subject SubjectRef `json:"subject,omitzero"`
// Picked are the answers given, in the words they were given under —
// [Words] renders them.
Picked []string `json:"picked,omitempty"`
// Labels are those answers as a person read them, kept beside the keys
// because a key is meaningless a month later and the question that gave it
// a meaning is gone.
Labels []string `json:"labels,omitempty"`
// Change is what the person said BESIDE the pick — "2, but keep the sqlite
// file as the source of truth" — and it is the half of an answer that a key
// can never carry.
Change string `json:"change,omitempty"`
// By is who decided; Stakes says whether it can be taken back; Scope says
// how long it lasts.
By DecidedBy `json:"by,omitempty"`
Stakes Stakes `json:"stakes,omitempty"`
Scope AnswerScope `json:"scope,omitempty"`
// Why is the person's own reason where they gave one, and it is what a
// preference is later written from. Empty is the ordinary case and nothing
// is drawn for it.
Why string `json:"why,omitempty"`
// Was is what this decision replaced, in the words it was read under, and it
// is filled on a CHANGED decision alone ([Answer.Revises]).
//
// THE LINE HAS TO SAY IT OR THE RECORD READS AS A CONTRADICTION. Two lines
// with the same head and different answers is exactly what a person changing
// their mind leaves behind, and a reader — the model, the mark reader, the
// person three days later — cannot tell that from the program having asked
// the same thing twice and got two answers. Measured on the Spark,
// 2026-09-11: a side-call read the record, found "Hello" and a file saying
// "Hola", and set about "fixing" the file.
Was []string `json:"was,omitempty"`
// At is when.
At time.Time `json:"at"`
}
DecisionRecord is one decision, kept.
IT IS NOT Decision AND IT IS NOT PendingDecision, and all three are spelled apart on purpose: Decision is the principal's answer about what a session should do next, PendingDecision is a question still waiting on somebody, and this is a question that has been answered and will not be asked again. pending.go draws the same distinction between the first two in its own words.
THE RECORD IS THE FIRST RUNG OF THE LADDER. Before an asker may put anything to a person, it reads this: a question a record already answers is refused with what was decided (Question.Check), which is the difference between a program that learns what somebody wants and one that asks them every morning.
func ReadDecisions ¶
func ReadDecisions(sessionDir string) ([]DecisionRecord, error)
ReadDecisions reads one session's record, oldest first. A folder with no record is an empty slice and no error, which is every session that has not yet decided anything.
func (DecisionRecord) Line ¶
func (r DecisionRecord) Line() string
Line is one decision on one line, and it is the whole rendering this package does of a record: head, what was picked, what was said with it, who decided, when, and whether it can be taken back.
IT IS ONE LINE BECAUSE IT IS READ IN BULK. The model carries the whole record in its context before it asks anything (DecisionsSection), and a person reads it as a list under a question. A rendering that ran to a paragraph would be a record nobody could hold in their head, which is the same as no record at all.
THE EMPTINESS LAW APPLIES TO EVERY SEGMENT. No change said, no `with:`; no reason, no reason; an unknown decider, no attribution at all.
func (DecisionRecord) LineClauses ¶
func (r DecisionRecord) LineClauses() []DecisionClause
LineClauses is DecisionRecord.Line before it is joined.
A NARROW ROW GIVES UP A WHOLE CLAUSE AND NEVER CUTS THE LINE FROM THE RIGHT. Cutting is what a receipt did before this existed, and the tail is where everything a person cannot infer lives: at a hundred columns a long `with:` clause took `· you · 14:02 · c change` off the end with it, so the one line left behind by an answer stopped saying who gave it, when, or that it could still be changed. The rank says what is actually worth keeping:
- the head and what was picked are the record itself and are never given up — a row with no room for them is cut rather than emptied;
- WHO DECIDED is never given up either. It is the one thing on the line nobody can work out for themselves, and it is what keeps a receipt from reading as something this person did: `another window` and `codeaf, on your settings` are the whole reason the field exists;
- `cannot change` stays for the same kind of reason — it is a LIMIT rather than a detail, and a row that dropped it would read as a decision somebody could still walk back;
- the change said beside the pick goes first, because it is the one clause the transcript and `decisions.jsonl` both still carry in full;
- then the time, which is the only clause on the line a person can usually get from where the row is sitting.
func (DecisionRecord) Reversible ¶
func (r DecisionRecord) Reversible() bool
Reversible reports whether this decision can still be taken back. A record that says otherwise reads `cannot change` rather than offering a key that would fail.
func (DecisionRecord) Words ¶
func (r DecisionRecord) Words() string
Words is what was picked, as a person read it: the labels where the record kept them, and the bare keys where it did not.
type DecisionVerb ¶
type DecisionVerb string
DecisionVerb is one of the three answers there are to a turn that stopped.
const ( // DecideCarryOn re-opens the turn on Brief. DecideCarryOn DecisionVerb = "carry on" // DecideDone says the ask is finished and the turn may end. DecideDone DecisionVerb = "done" // DecideStop ends the session's work and says why, in Reason. DecideStop DecisionVerb = "stop" )
type DelegateReport ¶
type DelegateReport struct {
Rows []DelegateRow
}
DelegateReport is the programs this conversation can hand work to, as the surface draws its command rows from them.
type DelegateRow ¶
type DelegateRow struct {
Name string
Description string
// Lands is delegate.LandsTree or delegate.LandsText.
Lands string
}
DelegateRow is one program as a surface lists it: the command word, the sentence under it, and what it leaves behind.
type DelegateUnknownError ¶
DelegateUnknownError is the refusal for a `via` or a command naming no program this build carries. It names the ones it does, sorted, so the next attempt has the words in front of it.
func (DelegateUnknownError) Error ¶
func (e DelegateUnknownError) Error() string
type Dial ¶
type Dial struct {
Min float64 `json:"min"`
Max float64 `json:"max"`
Default float64 `json:"default"`
Labels []string `json:"labels,omitempty"`
}
Dial is a number on a range. Labels name the ends and any marked points along it, so a person reads words rather than a bare number.
type DisplayEntry ¶
type DisplayEntry struct {
// Role is "user" | "assistant" | "tool" | "note" | "aside".
//
// "note" is a system-injected marker a surface draws as a rule of its own — a
// compaction summary is the one that exists.
//
// "aside" is a line the SESSION WROTE and the person did not: a task's
// completion note, a job's exit, a resume's account of what an interrupt left
// behind (agent.go's [Agent.enqueueNote]). It rides the user role in the
// transcript because that is the only role the model can be told something
// in, and it is separated here because a surface that drew it as a user
// message would be putting words in somebody's mouth — words that, live, that
// same surface deliberately never draws. It is answered from the journal's own
// mark, so a line from a file written before the mark existed still arrives as
// "user", which is exactly what it always was.
Role string
// Answer marks a completed tool-free response or an explicit human update. This
// boundary survives replay so a later response cannot demote its message.
Answer bool
// Addressed identifies an explicit update to the person, independently of
// completion. Interrupted updates remain readable without claiming success.
Addressed bool
Interrupted bool
Text string
Tool string // set when the entry is one call in a batch
Hint string // the call's gloss, as the tool cluster rendered it
// CallID is the provider's own identity for a tool entry's call, exactly as
// the record holds it, and "" for every entry that is not a call.
//
// IT IS THE ONLY THING A LIVE END CAN PAIR ON. A page opened on work already
// running draws its rows out of the record and then keeps listening; the end
// that arrives a second later has to land on the row that is already there,
// or the same call is drawn twice — once running forever, once finished. The
// id is what the two halves have in common, so it is carried rather than
// dropped at the shaping.
CallID string
// Answered is whether the record already holds the RESULT of this call.
//
// IT IS DERIVED FROM AN ABSENCE, and the absence is load-bearing: the
// assistant message is journaled BEFORE its batch runs (loop.go), so a
// session read while a call is in flight names the asking and nothing else. A
// surface that drew every recorded call as finished would tell a person the
// work is further along than it is.
//
// It is a field of its own rather than `Output != ""` because a call that
// returned nothing and a call that has not returned are different facts and a
// page speaks about them differently.
Answered bool
// Args and Output are a TOOL entry's payload, in exactly the two shapes a
// live surface already holds them in ([Event.Args] and [Event.Output]): the
// arguments the model sent, compacted onto one line and capped, and the text
// of the result that answered them, capped rune-safe for display.
//
// They are populated for a tool entry whenever the journal carries them,
// which is every session file this build writes — the arguments ride the
// assistant message's tool_calls and the result is the tool message keyed by
// the same id. Both are "" otherwise: for every entry that is not a call, for
// a call whose result never reached the file (a session killed mid-batch), and
// for a file written before either was journaled.
//
// EMPTY MEANS NO PAYLOAD, and a surface must read it that way rather than as
// an empty result: a replayed row with nothing behind it has nothing to
// expand, and offering an expansion that opens on a blank is the defect this
// field exists to end.
//
// CONTRACT, inherited from [Event.Output]: Output is FOR DISPLAY ONLY. It is a
// capped copy, never the result the model read.
Args string
Output string
// Caption and CaptionCategory are WHAT THE NARRATOR SAID ABOUT THE BATCH
// THIS CALL OPENED, and the family of work it named (caption.go,
// actioncategory.go). They are set on the batch's FIRST call and on nothing
// else, which is the same anchor the live [Event] carries, so a page built
// out of the record keys the step exactly where a page built out of the
// stream does.
//
// THEY ARE THE REASON A REOPENED CONVERSATION READS AS ITSELF. Without them
// a surface recomposes a title from the tool names — "running 1 command"
// where the person had been reading "starting the local server" — and draws
// the family those names imply, so a step the narrator called a `test`
// becomes a `run` the moment the file is read back.
//
// Both are empty for every entry that is not a batch anchor, for every batch
// the narrator never spoke about, and for every file written before the
// `caption` line existed. A surface reads that emptiness as "recompose", not
// as "draw nothing".
Caption string
CaptionCategory ActionCategory
// Took is HOW LONG THIS CALL'S OWN WORK RAN, from begin to end of its
// Execute — the same figure EventToolFinished carries live.
//
// IT IS WHY A REOPENED PAGE STILL SAYS WHAT A CALL TOOK. The live stream
// writes the figure onto the row as the call finishes; a page built out of
// the record after the batch has no stream to watch, and without this field
// the row came back with Args and Output but no duration. Zero when the
// journal never recorded one (every file written before the `took` line, a
// call that never finished), which a surface reads as "say nothing" by the
// emptiness law — the same reading toolview.go's [elapsedWord] already makes
// of a live row that never got EventToolFinished.
Took time.Duration
// ImageRefs are the paths of the pictures a person's message carried, in the
// order they sit in it — what the journal wrote where the bytes would have
// been (see [journalPart]). It is what lets a replayed message mark its
// attachments the way the live surface does, "[photo.png]", instead of
// showing the words alone as though nothing had been attached.
//
// Nil for every message that carried none, and for a session with no file:
// the paths are the JOURNAL's record, and a conversation that lives only in
// memory never wrote one.
ImageRefs []string
// ReplyTags label the assistant entry that answers finished task notes. They
// are nil on every ordinary reply.
ReplyTags []TaskReplyTag
// Steer marks a user entry that was typed INTO the turn it sits inside
// rather than starting one of its own (steer.go), and carries the instant it
// was sent.
//
// IT IS WHAT MAKES A TURN A TRUNK WITH ELBOWS. The entry that opened the turn
// is the question; every entry after it that carries this, up to the next
// question, is a correction the person made while the work was running — so a
// surface can draw the turn as one thing with the steers hanging off it
// instead of as a run of unrelated messages from somebody who kept
// interrupting themselves.
//
// It is answered from the JOURNAL's own mark, exactly as "aside" is: a
// message replayed out of a file written before steering existed carries nil
// and draws as the plain user line it always was. A steer that FELL THROUGH
// never appears here at all — it was never part of the turn, and what replays
// is the ordinary question it became (the record of the fall-through is its
// own line in the session file).
//
// Nil on every other entry, and on every session with no file to have kept a
// mark.
Steer *SteerMark
// Team is what a TEAM DELIVERY handed this conversation, line by line, on
// an "aside" that is one (teamshape.go): the manager's brief that started
// it (Kind [teams.KindStart]), a manager's note or directive, a teammate's
// post. It is what lets a surface draw the brief as a quoted card headed by
// who sent it rather than as the aside's first line. Nil on every other
// entry; the aside's Text still holds the whole delivery as the model read it.
Team []TeamLine
}
DisplayEntry is one journaled message shaped for surface replay: who spoke and what they said, with tool calls flattened to their gloss and carrying the payload the journal kept for them. Reasoning metadata is deliberately absent from this display shape even though the journal keeps it for model continuity; nothing about the wire itself is shown to the person.
type DocumentAnswer ¶ added in v0.4.0
DocumentAnswer is one document read once: the rung that produced the text and the text itself.
func ReadDocument ¶ added in v0.4.0
func ReadDocument(ctx context.Context, read DocumentRead) (DocumentAnswer, error)
ReadDocument is the whole billed road behind read_document, as a plain function: classify the file, walk the rung plan, bill each rung as it answers, and hand back the extraction. It exists so the belt's tool and the command line's doc door cannot drift — both call this, and neither re-walks the road beside it.
type DocumentParser ¶
type DocumentParser interface {
ParseDocument(context.Context, provider.DocumentRequest) (*provider.DocumentResponse, error)
}
DocumentParser is the one call read_document makes: a file in, its text out.
It is provider.Client.ParseDocument's signature VERBATIM rather than a simplified one of this package's own, for the reason MediaGenerator states at its own declaration: the seam carries the wire's shape, so nothing between here and the provider can drift, and no adapter exists to drift in. It is an interface rather than the concrete client so a test drives a scripted parser and never opens a socket.
func NewDocumentParser ¶ added in v0.4.0
func NewDocumentParser(config Config) (DocumentParser, error)
NewDocumentParser builds the parser the doc road bills through, for a caller with no session: the command line's doc door hands over a Config carrying the account it read off the profile, and gets back the same client the belt's read_document builds on first use — same key resolution, same seat-pin drop, same timeout. A missing key is an error that says so, exactly as it does on the belt.
func NewDocumentParserFromProfile ¶ added in v0.4.0
func NewDocumentParserFromProfile(settings config.Config) (DocumentParser, error)
NewDocumentParserFromProfile is NewDocumentParser for a door that holds the profile rather than a session: it lifts the account off the loaded settings here, so no command-line file has to spell a session Config of its own — the audit law over cmd/codeaf reads every such literal as a run being built without governance, and a parser is not a run.
type DocumentRead ¶ added in v0.4.0
type DocumentRead struct {
// Path is the file, workspace-relative or absolute, and Workspace resolves
// it exactly as the belt's read resolves paths (resolveInWorkspace).
Path string
Question string
Engine string
Workspace string
// PagesFrom and PagesTo name the page range the LOCAL rung renders,
// one-based and inclusive, and are both zero for the whole document. They
// stay empty on the belt, where paging is the read law's offset/limit over
// the whole extraction; the command line uses them to read two pages of a
// document with a text layer without printing four hundred. A billed parse
// returns no page boundaries, so a range on a file the local rung cannot
// read is refused rather than silently ignored.
PagesFrom, PagesTo int
Parser func() (DocumentParser, error)
ModelOf func(kind string) string
Account func(model string, usage *ai.Usage)
Recall func(key string) (rung, text string, ok bool)
Keep func(key, rung, text string)
}
DocumentRead is everything one billed document read needs, named so a caller with no session — the command line's doc door — can walk the exact road the belt's read_document tool walks: the same guards, the same local rung, the same rung plan, the same refusals, the same accounting.
Parser, ModelOf and Account are the session-shaped half: who parses, which model a parse is billed to, and where the bill goes. Recall and Keep are the paging memo, both optional — a one-shot caller passes neither, because nothing will ever ask it for page two.
type EarlierHistory ¶
type EarlierHistory struct {
// Entries is the region, oldest first, in the shape [Agent.Transcript] uses.
// Empty when there is nothing to offer.
Entries []DisplayEntry
// Floor is how many entries at the start of [Agent.Transcript] the region
// replaces. Zero whenever Entries is empty.
Floor int
}
EarlierHistory is the conversation a compaction pass edited away, and where the pass's own rewritten copy of it ends in the live transcript.
THE CONVERSATION, TOLD ONCE AND WHOLE, IS `Entries` FOLLOWED BY `Transcript()[Floor:]`. That is the contract, and it is a splice rather than a prefix because the pass does not delete the history it shortens — it rewrites it in place and journals the whole rewritten window again, so the same conversation is in the file twice: once as it happened, above the marker, and once with its tool results stubbed and its long runs of work folded, below. Drawing both would show the session to itself twice.
type Elsewhere ¶
type Elsewhere struct {
// Read is when this reading was taken, so a cache holding it can say how old
// it is without keeping a second stamp beside it.
Read time.Time
// contains filtered or unexported fields
}
Elsewhere is one reading of every OTHER window open on one project.
The rows are unexported because nothing outside this package should be able to read a claim without going through the methods that judge it — a caller holding raw presence rows is a caller one loop away from repeating a file's claim of liveness, which is the mistake world.go's first law exists to prevent.
func ElsewhereOf ¶
ElsewhereOf is Agent.Elsewhere asked by a surface that holds a conversation's TRANSCRIPT and not its agent: the reading of the bucket that conversation's folder is in, with that conversation left out.
IT EXISTS BECAUSE THE ORDINARY WINDOW HOLDS NO AGENT. Bare `codeaf` is a surface talking to this workspace's engine over a socket, and what it holds is a connection ([remote.Agent]), which has no reading of the disk to offer. The engine is on THIS machine, though, and the presence files are on this machine's disk beside the transcript the surface was handed — so the answer is the same arithmetic Agent.ElsewhereExcept does on its Place, done on the path: the transcript's folder is the session, and its parent is the bucket.
A transcript with no folder of its own has no bucket to look in and no id to leave out, and answers the empty reading, as a memory-only agent does.
func NewElsewhere ¶
NewElsewhere is a reading built from presence rows a caller already holds, with each window's name supplied rather than looked up.
IT APPLIES THE FRESHNESS RULE ITSELF and drops every row that fails it, which is the whole reason this door is safe to have. ReadProjectPresence already hands presence rows to anybody who asks, so the rows are not the secret — what this type is protecting is the JUDGEMENT, and a reading assembled out of claims nobody dated would let a caller smuggle a dead window past it. The clock is handed in for the same reason [readWorld]'s is: one instant, so two rows of one reading cannot age differently.
names may be nil, and a window it does not name has no name (see ElsewhereTask.Session).
func ReadElsewhere ¶
ReadElsewhere is every live window in ONE project bucket except the caller's own, with each window's name resolved once.
exclude is the caller's own session ids, on ReadProjectPresence's terms: the window a surface is being drawn in must never appear on it as somebody else.
IT IS SEVERAL IDS AND NOT ONE, because "the caller" stopped being one conversation. A process can hold several sessions on one project at once — one on screen and the rest open behind it — and every one of them writes the same presence file every other window reads. Excluding only the one in front would put this process's OWN other conversations on its own `away` rows as `another window`, and tell somebody to go to a window that is two keystrokes away in the terminal they are already sitting in.
func (Elsewhere) Any ¶
Any reports whether another window is open on this project at all. It is the cheapest form of the question and the one a surface asks before it decides whether a section exists.
func (Elsewhere) Runs ¶
func (e Elsewhere) Runs(entry TaskIndexEntry) bool
Runs reports whether one row of the project's index is work HAPPENING in another window at this instant.
IT IS SessionRow.Runs OVER A SET, and the join is the same one on (SessionID, ID) — the pair both files spell the same way on purpose. A row this answers false for is a record: either work that landed, or work that was under way when a window went and that nobody is left to finish.
IT ANSWERS FALSE FOR THIS SESSION'S OWN ROWS, always, because this session was excluded from the reading. That is deliberate and not a gap: a surface knows its own graph, which is a better answer about its own work than any file, and a second opinion here would be the one that disagreed with it.
func (Elsewhere) Tasks ¶
func (e Elsewhere) Tasks() []ElsewhereTask
Tasks is every piece of work the other windows have out, newest window first ([sortPresence]'s order, carried through).
THIS IS THE ONLY DOOR ONTO ORDINARY CROSS-WINDOW WORK, for the reason in this file's header: a task that has not landed has no row in the project's index, so a surface that only read the index would show another window's finished work and none of what it is doing now.
func (Elsewhere) Touching ¶
func (e Elsewhere) Touching(files []string) (touching, unknown []ElsewhereTask)
Touching is the work OTHER windows on this project have out that has already written one of these files, and — separately — the work that has said nothing about files at all.
The two lists are the two honest answers, and neither stands in for the other: touching is what is certainly in your way, unknown is what might be and cannot be asked. A surface saying "you are alone in this file" may only say it when BOTH are empty.
IT IS THE READING'S OWN ROWS AND NOTHING ELSE. An Elsewhere has already dropped this session and every stale window (NewElsewhere applies the freshness rule), so a caller cannot smuggle a dead claim past this. The world reader's rows reach it the same way: SessionRow.Presence values go through NewElsewhere and come out judged.
NO FILES ASKED IS NO QUESTION ASKED, and it answers nothing rather than handing back every window on the project.
type ElsewhereTask ¶
type ElsewhereTask struct {
// SessionID is the conversation holding it, which is the id
// [TaskIndexEntry.SessionID] records and the session folder is named.
SessionID string
// Session is what to CALL that window: the title it settled on, and "" for
// one nothing ever named. THE EMPTINESS LAW HOLDS: a window with no name has
// no name, and inventing one out of its id would put a string of hex where a
// person expects words. What a surface draws instead of it is the surface's
// own business.
Session string
// Task is the work itself, exactly as the other window described it.
Task PresenceTask
}
ElsewhereTask is one piece of work another window has out, with enough of the window on it to say whose it is.
It carries the presence row's task WHOLE rather than flattening it, because the fields a surface wants — the title, the state, when it began — are already spelled there and copying them out would be a second spelling of each.
type ErrSpendStopped ¶
type ErrSpendStopped struct{ Action string }
ErrSpendStopped is a call the guard did not make. Its text is the one sentence that says which line it met.
func (ErrSpendStopped) Error ¶
func (e ErrSpendStopped) Error() string
type Event ¶
type Event struct {
ReplayCursor ReplayCursor `json:",omitempty"`
ReplayObserved bool `json:"-"`
Discussion *QuestionDiscussion `json:",omitempty"`
Kind EventKind
// Addressed is a producer's declaration that streamed text is for the person.
// It does not imply a completed response and survives interruption.
Addressed bool `json:"Addressed,omitempty"`
Text string
ShortTitle string `json:"ShortTitle,omitempty"`
Tool string
Hint string
Err error
// Discard says an EventError ended a cut attempt whose streamed text was
// never a reply. The surface withdraws that attempt before drawing the
// error, as it does for EventRetrying, without counting another retry.
Discard bool `json:"Discard,omitempty"`
Usage Usage
TaskReplyTags []TaskReplyTag
// Skills is the ordered list of skill names this turn carried, on the
// notice that announces them (skillturn.go). IT IS THE FIELD AND NOT THE
// SENTENCE a surface reads: [Event.Text] says the same thing in words for
// a reader who draws notices as prose, and a surface that took the names
// back out of that sentence would break the first time somebody improved
// the wording or a skill name held a comma, and would break silently,
// because a test written against the same sentence agrees with it.
//
// AN ABSENT LIST MEANS UNKNOWN AND NOT NONE. The tag is omitempty because
// an event with no skills has to serialise as it did before this field
// existed, which is what keeps a new session and an older peer talking
// (internal/remote's wire tests). The cost is that a turn that carried
// nothing and a peer too old to send the field put the same bytes on the
// wire, so a surface may draw a non-empty list and must say nothing at all
// otherwise — a sentence like "no skills used" is a claim this field
// cannot support.
Skills []string `json:"Skills,omitempty"`
// Summarized is how many messages a compaction pass replaced with a
// summary, on [EventCompacted]; zero for a pass that only stubbed and
// folded. It is the field a surface decides by, never the hint's words:
// a pass that rewrote the person's own messages is the one whose line
// stays standing (internal/tui3's workfold.go), and a free pass folds
// with the rest of the turn's machinery. Omitted when zero, so an event
// with no summary serialises exactly as it did before this field, and a
// surface talking to an older engine folds every pass.
Summarized int `json:"Summarized,omitempty"`
// Category is the FAMILY OF WORK an EventCaption's sentence is about — one
// word from the closed list in actioncategory.go — and it is zero on every
// other kind.
//
// EMPTY IS THE NORMAL MISSING CASE AND NOT AN ERROR. The narrator is a cheap
// model asked for a prefix it may ignore, and a surface that receives none
// derives the family from the batch's own tool names
// ([ActionCategoryForTools]), which is deterministic and cannot be wrong
// about which hands were used. So this field REFINES a mark that is already
// correct; it never supplies one that would otherwise be missing.
//
// It rides the wire behind a json tag of its own so a peer built before it
// existed simply does not see it (internal/remote's [EventWire] embeds this
// struct whole), and a caption saved by an older build replays with an empty
// one and derives the same mark it always drew.
Category ActionCategory `json:"Category,omitempty"`
// Unchanged says an [EventCompacted] pass left the transcript exactly as it
// found it: nothing was old enough to stub and nothing was foldable, so the
// region above the conversation did not move and neither did the floor
// beneath it. It is false on every other kind and on every pass that really
// edited something.
//
// THE ZERO VALUE IS "A PASS HAPPENED", and that polarity is the whole reason
// this is a field rather than a reading of Hint. EventCompacted is sent on
// BOTH paths by promise, because a surface opens a row on EventCompacting
// and has to be able to settle it whatever the pass found. So one value
// carried two meanings and the failing one was silent: a surface handed its
// scrollback over to a replacement that had not happened, and declared the
// conversation finished with a good part of it undrawn and unreachable.
//
// It rides the wire behind a json tag of its own, so a peer built before it
// existed does not send it, reads false, and behaves exactly as it always
// did (internal/remote embeds this struct whole).
//
// NO PASS IN THIS BUILD SETS IT. A pass that finds nothing now sends neither
// EventCompacting nor EventCompacted, so there is no row to settle. The field
// stays, and a surface still honours it, because a remote engine built
// between the two changes announces every pass and settles a refused one
// with it.
Unchanged bool `json:"Unchanged,omitempty"`
// Args is the tool call's arguments rendered for display: the JSON the
// model sent, compacted to one line and capped. It is set on
// EventToolBegin, EventToolEnd and EventToolFailed. Arguments that do not
// parse as JSON pass through as the raw text — a malformed call is still a
// call the person should be able to look at.
//
// THE CONTRACT IS THAT THIS STAYS PARSEABLE WHENEVER THE WIRE ARGUMENTS
// WERE, at every size. The cap ([argsLimit]) is spent INSIDE the oversized
// string values, each of which then ends in the marker [capBytes] writes —
// `… (12345 more bytes)` — rather than by cutting the text, which would end
// a 20k write's payload in the middle of a string literal and leave every
// reader downstream calling a well-formed call malformed.
//
// So a field a surface reads back out of this may be SHORTER than the one
// the model sent, and says so in its own last bytes. Anything derived from
// one is a floor rather than a figure: a capped write's line count is "at
// least this many", and internal/tui3 spells that with a trailing `+`.
Args string
// BeltStepHandled is present only for a run worker that opted into the
// step boundary handshake. Its owner closes it after recording this end
// event and applying the run's limits and notes. Cancellation releases a
// belt whose reader failed, and this local handshake never goes on wire.
BeltStepHandled chan<- struct{} `json:"-"`
// Output is the tool's result text on EventToolEnd and EventToolFailed,
// verbatim up to a cap and then marked "… (N more bytes)".
//
// CONTRACT: Output is FOR DISPLAY EXPANSION ONLY. It is not the result.
// The wire result — what the model reads, what the transcript records — is
// unchanged and complete; this field is a capped copy for a surface that
// wants to show more than Hint. A surface must never treat it as the tool's
// output for any purpose other than showing it to a person.
Output string
// HarnessMade says this step's failure was written by the HARNESS and not by
// the world the model reached for: a hand that was withdrawn (withdrawn.go),
// a door that refused the call (consent.go and the rest of the pre-action
// chain). It is set on EventToolFailed and on nothing else.
//
// IT EXISTS FOR THE COUNTERS. A stuck detector's whole claim is that a step
// which taught nothing was a step the model had no business taking, and that
// claim is false when the harness wrote the answer itself — measured in
// SWE-Marathon s4, where the harness withdrew `bash`, answered eight retries
// with "Unknown tool", and then injected three [stuck] notes blaming the
// model for the retries (withdrawn.go states the whole failure). A surface
// may show it or ignore it; the runner reads it to keep the harness's own
// steps out of the model's ledger ([runTaskChild]).
HarnessMade bool
// Refused says a DOOR said no to an action the model attempted: the call was
// well formed, it named a tool on the belt, and a pre-action citizen (the
// approval gate, a write or ground guard) refused it before it ran. It is set
// on EventToolFailed, only together with HarnessMade, and only at the one
// place every veto passes through ([episode.preAction] names who refused).
//
// IT SEPARATES TWO ANSWERS THE HARNESS WRITES. A correction about the FORM of
// a reply (one call per response, a malformed call, a withdrawn tool, a held
// process rule) is addressed to the worker and nothing was attempted on the
// world: HarnessMade alone. A refused door is something the worker TRIED, and
// a person steering a run wants to see that it was tried and refused. The
// fact is kept here, where the attempted action is known, so no reader has to
// tell the two apart by the words of the answer.
Refused bool
// ID names one EventConsentRequest, and is the token a surface hands back
// to [Agent.ResolveConsent]. It is zero on every other kind but
// EventHarnessOffer, whose own id goes back through
// [Agent.ResolveHarness] — two lanes, two counters, and one field, because
// "which question" is the same question for both of them.
ID uint64
// CallID is the PROVIDER's id for the tool call a tool lifecycle event
// (forming, announced, begin, finished, end or failed), an
// EventConsentRequest or an EventCaption is about — the same string the tool
// result carries — and is empty on every other kind. It is empty on a forming
// event too until the wire has sent one, which is the first fragment in
// practice and nothing the consumer may assume.
//
// ON A CAPTION IT IS THE BATCH'S ANCHOR: the id of the call the batch opened
// with, which is how a surface knows WHICH STEP the sentence is about. The
// narrator answers on a goroutine that can be descheduled between checking
// that its batch is still open and reaching the hub, so a caption can arrive
// after its batch ended and the next one began. A surface keying on "the
// newest tool row" then retitles the running step with a sentence about the
// finished one; keyed by this id it drops news about work it is no longer
// holding. An event with no anchor is an engine built before this, and a
// surface may serve it by recency exactly as it always did.
//
// ON A CONSENT REQUEST IT IS WHICH CALL IS BEING ASKED ABOUT. A surface pairs
// the question to the row it draws the question under, and the card reads the
// command it is about to remember off that row — so a question paired by tool
// name alone can, with two bash calls in flight, show one command and bank a
// standing rule for the other (internal/session's consent.go).
//
// It is on BOTH ends of that pair on purpose: forming and announced are two
// states of one call, and the id is what lets a surface say so. Without it
// the announcement can only be paired by tool name, and a batch of parallel
// calls of the same tool has no name to tell its rows apart by.
//
// It is not [Event.ID] because that field is the consent lane's own token, a
// uint64 this session mints; these are two different names for two different
// things and folding them would make "which call" and "which question"
// the same field with two answers.
CallID string
// ArgsText is the RAW, PARTIAL arguments text of a forming call: exactly what
// the provider has streamed so far, uncompacted and unparsed. It is set on
// EventToolForming and empty everywhere else — Args is the display JSON of a
// WHOLE call, and half of a JSON object is not that.
//
// It is CUMULATIVE: every fragment carries the whole text that has arrived so
// far, not the piece that just landed, so a surface keeping it replaces what
// it held rather than appending to it. It is capped at [formingArgsLimit]
// from the FRONT, and Bytes beside it is the honest size of the whole.
//
// A surface may show it, cut it, or ignore it. NOTHING MAY UNMARSHAL IT — and
// nothing needs to: [PartialString] is the tolerant read of one field's
// streamed text, and it is one scanner in one place rather than a second
// parser per surface.
ArgsText string
// Took is how long ONE tool call's own work took, on EventToolFinished and
// zero on every other kind. It is measured around the tool's execution and
// around nothing else: not the wait for a consent question, and not the wait
// for the rest of the batch.
Took time.Duration
// Bytes is how much of a forming call's arguments has arrived. It is the
// length of ArgsText, carried as its own field so a surface can show progress
// ("write · 4.2 KB") without measuring text it may have chosen not to keep.
Bytes int
// Harness progress fields ride on EventHarnessProgress alone. They are flat
// because the event is already the transport envelope and every field is a
// short fact a surface may independently omit.
Goal string
Phase string
Attempt int
Attempts int
ThoughtTail string
Stalled bool
// Step is one finished step of a RUNNING sub-harness, on EventHarnessStep
// alone and nil on every other kind. It is the walk's own trail entry rather
// than a copy of the parts of it a surface might want, so the row drawn while
// the run happens and the row on the card read back afterwards are rendered
// from one fact (subharness.StepLine).
Step *subharness.Trail
// Entry is one host call a running SUBHARNESS just made, on
// EventSubharnessStep alone and nil on every other kind
// (internal/exec's JournalEntry). It is the journal's own entry rather than a
// copy of the parts of it a surface might want, for the reason Step above is
// the trail's: the row drawn while the run happens and the row read back out
// of the journal afterwards are one fact rendered twice.
Entry *exec.JournalEntry
// Task carries one EventTaskProposal or EventTaskUpdate's payload
// (task_contract.go). It is nil on every other kind, and the ID inside it
// is the token a surface hands back to [Agent.ResolveTask].
Task *TaskNotice
// Job carries one EventJobUpdate's payload (jobnotice.go). It is nil on every
// other kind, and the Id inside it is the job's OWN number — the one
// `jobs output 3` and `jobs kill 3` already take — rather than a second id
// minted somewhere else to keep it from colliding with a task's.
Job *JobNotice
// TaskPhase carries one EventTaskPhase's payload (task_contract.go): which
// running node moved into which of its three lives. It is nil on every
// other kind, and it is its OWN payload rather than more fields on
// TaskNotice because a phase is not a row — it names no state, no elapsed
// and no cost, and a surface that mistook one for an update would redraw a
// card from a value that never carried those.
TaskPhase *TaskPhaseNotice
// Standing carries one EventStandingProposal or EventStandingUpdate's payload
// (standing_contract.go). It is nil on every other kind.
Standing *StandingNotice
// Subharness carries one EventSubharnessProposal's intake card
// (subharness_contract.go). It is nil on every other kind, and the ID beside
// it is the token a surface hands back to [Agent.ResolveSubharness].
Subharness *SubharnessCard
// Retry carries one [EventRetrying]'s payload in parts (retrynews.go): which
// model was being asked, how far into its patience the step is, why the
// attempt is void, and — when the step is moving — which model the rest of
// the reply will come from. It is nil on every other kind.
//
// It rides behind a json tag of its own so a peer built before it existed
// simply does not see it (internal/remote's [EventWire] embeds this struct
// whole), and an older engine's retry arrives with none — which is the same
// thing this build's surface must already handle, because [Event.Text] is
// still the whole line and always has been.
Retry *RetryNews `json:"Retry,omitempty"`
// Steer carries one sentence spliced into a running turn, on
// EventSteerAccepted, EventSteerConsumed and EventSteerFellThrough alone; it
// is nil on every other kind (steer.go). The same [SteerNote] value rides
// all three, so a surface pairs the outcome with the row it drew on the
// acceptance by [SteerNote.ID] and never by matching the words.
Steer *SteerNote
// Rule is the approval policy's own phrasing of why a call is being asked
// about — `bash pattern "rm -rf *"`, `tool "edit"`, `default`. It is set on
// EventConsentRequest and empty elsewhere. The wording is the policy's
// (internal/approval) so that every surface says the same sentence about the
// same rule instead of deriving one.
Rule string
// Wait is how silence is held on EventConsentRequest: ConsentWaiting means
// the question stays up. A surface clock that recorded "denied" after a
// few seconds was F41, and this field is how the engine says that is not
// the mode. Empty on every other kind.
Wait string
// Memo says whether a ConsentToolSession answer to this question WOULD DO
// ANYTHING. It is set on EventConsentRequest and false everywhere else.
//
// It exists because the consent lane carries two different questions. The
// gate's question is about a TOOL, so "and stop asking me about this tool"
// is a real answer and this is true. The stuck question (recovery.go)
// borrows the same lane to ask about a TURN, and a tool-session scope on it
// is dropped on the floor — which, without this field, a surface could not
// know, and so offered an option that silently did nothing. An offer that
// is inert must not be on screen: it is worse than a missing key, because a
// person who presses it believes they have changed something.
Memo bool
// Count is how many times the thing this event is about has happened. It is
// set on EventNudge — the number of repetitions that earned the nudge — and
// zero everywhere else, which is why it is a plain int rather than a pointer:
// no other kind has a count, and "0" is not a count any kind reports.
Count int
// The five fields of the three connect kinds (connect.go). They are flat
// rather than a payload struct because the three events between them carry
// five short strings and a bool, and a surface drawing the sequence reads
// them one after another off the same event.
//
// ConnectID names one EventConnectAsk and is the token handed back to
// [Agent.ResolveConnect]. Service is the account's id — "google" — on all
// three kinds; ServiceName is the word a person reads — "Google" — on the
// ask. AuthURL is the page to open, on EventConnectAuth only. Account and
// Failed are the outcome, on EventConnectDone only.
ConnectID string
Service string
ServiceName string
AuthURL string
Account string
Failed bool
// NeedsKey rides on EventConnectAsk alone and says that this account needs
// a typed answer: a key the person already holds, or the one thing the
// service's address is missing. A surface hands it back through
// [Agent.ResolveConnectKey]; a plain yes means nothing here, because the
// answer has not been given yet.
//
// A key is followed by EventConnectDone. An address answer is followed by
// the ordinary EventConnectAuth browser trip.
NeedsKey bool
// Model is which model a harness offer would run on, and the one it did run
// on: set on EventHarnessOffer and EventHarnessRun, empty everywhere else
// and empty on both of those when the turn named no model (harness.go).
//
// It is a RESOLVED ID and never the person's word — "opus" arrives here as
// anthropic/claude-opus-5 — so a surface draws what will actually be sent
// rather than what somebody typed.
Model string
// Harness is the page one EventHarnessDesignDone is asking about, and nil on
// every other kind. It is the whole harness rather than a rendering of one
// because the rendering is shared (subharness.CardLines): a surface draws the
// same card the tool prints and the panel lists, and a session that shipped
// pre-rendered lines would have made itself the second renderer.
//
// It is a POINTER so that "no design here" is spelled once, and the value it
// points at is this event's own copy — nothing else holds it, and answering
// the question is what decides whether it is ever written down.
Harness *subharness.Harness
// Question is the whole decision on EventQuestion and
// EventQuestionWithdrawn, and nil on every other kind (question.go). It is
// a POINTER so that "no question here" is spelled once, and the value it
// points at is this event's own copy — nothing else holds it, and the
// answer is what decides whether it is ever written down.
Question *Question
// Answer is the whole answer on EventQuestionAnswered, and nil on every
// other kind. It is the same value [Agent.ResolveQuestion] was handed, after
// the door filled in what the caller left out.
Answer *Answer
// ModelNote is why a model the turn NAMED is not in Model: a word no model
// here answers to, a word too many of them answer to. It is set on
// EventHarnessOffer alone.
//
// The words are this package's, on the same terms Rule's are: a note about
// a model this session could not find should read the same on every
// surface, and a surface that phrased it itself would be writing a sentence
// about a catalog it did not consult.
ModelNote string
}
Event is one observable thing in a turn. A Submit returns a channel of them, closed after EventTurnDone or EventError.
A STEER'S CHANNEL IS THE ONE EXCEPTION, and it is exact: Agent.Steer hands back a stream that outlives the turn it was sent into when the steer falls through, so EventSteerFellThrough arrives AFTER that turn's EventTurnDone or EventError, and the turn the words then start speaks on the same channel (steer.go says why). A caller that reads to close — which is every caller today — sees all of it in order and needs no second rule; a caller that stops at the terminal event stops at the terminal event of the FIRST turn.
type EventKind ¶
type EventKind int
EventKind names one thing the person can see happening.
const ( // EventTextDelta carries one streamed chunk of the assistant's reply in Text. EventTextDelta EventKind = iota // EventThinking says the model is reasoning; it carries no text. EventThinking // EventToolBegin carries the tool name in Tool and a person-readable gloss // in Hint — "read internal/session/session.go", "bash go build ./…". Args // carries the call's arguments for a surface that expands the row; Output // is empty, the call has not run yet. EventToolBegin // EventToolEnd carries the tool name, a short result hint (often empty), // and the call's Args and Output for expansion. EventToolEnd // EventToolFailed carries the tool name and why, with the same Args and // Output as EventToolEnd — a failure is the one result worth reading in // full, and the surface has it here without asking. EventToolFailed // EventTurnDone ends one Submit's stream; Usage is the turn's total. EventTurnDone // EventError ends the turn abnormally; Err says why. EventError // EventCompacting says a compaction pass has edited the transcript, which is // work a surface should show rather than silence. Hint sizes the pass // ("compacting ~84k tokens"). // // EventCompacted always follows it — a surface opens a row on this one and // settles it on that one. A pass that found nothing to do sends neither, so // it opens no row that would need settling (loop.go's [Agent.compactWithPolicy]); // `/compact` hears that from [ErrNothingToCompact] instead. Most passes are // two mechanical walks over messages this session already holds, so what // they cost is a lock; only a pass those walks cannot finish writes a summary // with the conversation's model (compact_summary.go). EventCompacting // EventCompacted marks a compaction pass; Hint summarizes // ("compacted from ~84k tokens, kept last ~20k"). [Event.Unchanged] is only // ever set on it by a peer built before a no-op pass went silent. EventCompacted // EventReasoning carries one streamed chunk of the model's REASONING in // Text, for the models that put their working on the wire (OpenRouter's // "reasoning", the DeepSeek family's "reasoning_content"). // // It follows the EventThinking that opened the run rather than replacing it: // a surface that only draws "thinking…" ignores this kind and is unchanged, // and a surface that shows the thought has the words and the boundary both. // The text is NOT part of the answer and never enters the partial reply. // Completed steps record it as hidden provider metadata and replay it under // the same wire field, so the next tool step can continue the model's work // without presenting that work as something the assistant said out loud. EventReasoning // EventConsentRequest asks the person whether one tool call may run // (consent.go). It carries the call's ID, Tool, Args and gloss in Hint, and // the policy's own phrasing of why it is asking in Rule. Wait is // ConsentWaiting: silence is not a no. // // It is a QUESTION, not a report: the call is blocked inside the tool batch // until [Agent.ResolveConsent] answers it or the turn's context dies, and a // surface that ignores this kind leaves the turn waiting until the person // interrupts. It arrives AFTER the batch's EventToolBegin rows, so a surface // attaches the question to the row it already drew for that call. EventConsentRequest // EventTitleChanged carries the session's name in Text (title.go). It fires // at most once per session — after the first completed turn, when the // session had no name yet. EventTitleChanged // EventToolAnnounced says one tool call has finished ARRIVING — the model // has sent the whole instruction — while the response it rides on is still // streaming. It carries the same Tool, Hint and Args EventToolBegin will, // and no Output: nothing has run. // // EventTaskProposal asks the person whether one groomed piece of work may // become a task node (task.go). It carries the proposal in Task: title, // the two-or-three-line summary, the full brief, and the auto-approve // deadline. // // It is a QUESTION with a CLOCK, not a report: the propose_task call is // blocked until [Agent.ResolveTask] answers it or the deadline passes, and // the deadline passing means APPROVED — the surface is the person's chance // to redirect, never a gate the work waits on forever. A surface with no // answer box for this kind still works: the countdown approves. EventTaskProposal // EventTaskUpdate reports one task node's progress (task.go): Task carries // the state (running, done, failed), the elapsed time, and on completion // the report, the changed files, and the merge outcome. It is a report, // never a question; the first update (running) arrives as the proposal // resolves. EventTaskUpdate // EventToolAnnounced says one tool call has finished ARRIVING — the model // has sent the whole instruction — while the response it rides on is still // streaming. It carries the same Tool, Hint and Args EventToolBegin will, // the CallID its forming events carried, and no Output: nothing has run. // // It is the difference between "asked for" and "started", and it exists // because those two moments can be seconds apart. A mutating call is // announced here and does not begin until the response completes and the // batch starts (loop.go's safety law), so a surface that only had // EventToolBegin had to choose between drawing nothing for that gap or // drawing a spinner for work that had not started. Both are lies; this is // the third option. // // EventToolBegin keeps its exact meaning: EXECUTION STARTED. Every call that // is announced is also begun, in the same order, so a surface that ignores // this kind is unchanged — and a provider that never announces (a // non-streaming endpoint) simply sends no event of this kind. EventToolAnnounced // EventToolForming says one tool call is still ARRIVING — the model is // spelling it out and has not finished. It is the phase BEFORE // EventToolAnnounced, and it exists because that gap is not instant: a long // write or a groomed propose_task takes seconds to stream, and a surface // with only the announcement draws nothing at all for them. // // It carries CallID (the call's id once the wire has said one), Tool (the // name once its delta has landed), Hint (a best-effort gloss built from the // argument fields that have CLOSED so far — "write internal/foo.go" while the // body of the file is still arriving), ArgsText (the raw partial arguments) // and Bytes (how much of them has arrived). // // NOTHING HERE IS AN INSTRUCTION. ArgsText is half-sent JSON and is never // parsed into Args; Hint is a scan, not an unmarshal; and forming NEVER // implies execution — a formed call has not been announced, let alone begun, // let alone consented to. // // ORDERING: forming (zero or more, per call) → EventToolAnnounced → // EventToolBegin, keyed by CallID. Every call that forms is announced and // begun in that order; calls in a parallel batch interleave with each other, // but each call's own sequence holds. A non-streaming provider forms nothing, // so a surface that ignores this kind is exactly what it was. EventToolForming // EventGuardianAllowed says a call the policy would have ASKED about ran // because the guardian model vouched for it (guardian.go). It carries the // Tool, the call's gloss in Hint and Args, and the rule that would have // prompted in Rule. // // It is an ANNOTATION, not a question and not a result: the row it belongs to // is the ordinary tool row, and this is the dim line beside it saying who // answered instead of the person. A surface that ignores this kind shows a // call that simply ran, which is what it did — but a gate that answers on // somebody's behalf and says nothing about it is a gate nobody can audit, so // the event exists whether or not a given surface draws it. EventGuardianAllowed // EventNudge says the turn has been caught going in circles (looped.go): Tool // is the call that repeated, Count is how many times. A surface renders it as // "stuck? nudged · <tool> ×N" for the warning rungs. // // The first two nudges are notes in the transcript, not errors or refusals. // Past that ceiling the same event accompanies the checkpoint take-over rather // than a third note. This event is how a person gets to SEE either happen. EventNudge // EventNotice carries one line in Text about what the turn's own machinery is // doing to make the request land — not the model's words, and not a failure. // // Its one source today is the provider's endpoint-refusal chain // (internal/provider's endpoints.go): "Retry 1/3: removed max_tokens", // "Retry 3/3: Falling back to <model>". Those retries change the shape of the // request a person asked for, so a surface that drew nothing for them would // be showing an answer without showing what it cost to get one. // // It is a NOTE, like EventNudge: dim, one line, never an interruption. It can // arrive before any text on the turn, and a turn may end in EventError with // several of these already on screen — that sequence is the chain trying // everything it had and saying so. EventNotice // EventConnectAsk asks the person whether one of their accounts may be // connected (connect.go). It carries the id the answer is handed back with in // ConnectID, and the account in Service and ServiceName — "google" and // "Google", the word the tools use and the word a person reads. // // It is a QUESTION, and the same kind of question a consent prompt is: the // use_service call is blocked inside the tool batch until // [Agent.ResolveConnect] answers it, the five-minute clock runs out, or the // turn's context dies. A surface that ignores this kind leaves the call // waiting until one of those three happens, and a clock that runs out is a NO. EventConnectAsk // EventConnectAuth carries the page the person opens to say yes to the // service named in Service: the address is in AuthURL. // // It is an INSTRUCTION to the surface — open this — and it arrives only after // the person has already agreed to connect the account. It is followed by // exactly one EventConnectDone, whatever happens next. EventConnectAuth // EventConnectDone ends one connect attempt for the service in Service: // Account is the address it connected as, and Failed says it did not connect // at all. The two are exclusive — a failure carries no account — and a // person who simply walked away shows up here as a failure, because from // this side an attempt nobody finished and an attempt that broke are the same // fact: nothing is connected. EventConnectDone // EventHarnessOffer asks the person whether one sub-harness should take this // turn (harness.go). It carries the id the answer is handed back with in ID, // the harness's name in Text, and its one-sentence description in Hint. // // Model is the model the turn NAMED — "research this with opus" — resolved // to an id this install has, and empty when nobody said. ModelNote is the // other half of that: a word that named no model here, said in words a // surface prints as it stands. Neither is a refusal; the offer is the same // offer either way. // // It is a QUESTION, and the quietest kind on this list: the turn is held // before its first request until [Agent.ResolveHarness] answers it or the // turn's context dies, and NO is free — the turn the person typed runs // exactly as it would have. A surface that ignores this kind would leave the // turn waiting, which is why the offer is never raised unless somebody has // said they are watching (Config.AskConsent). EventHarnessOffer // EventHarnessRun says the person said yes and the harness named in Text has // the turn. Hint is its description, and Model is what it is running on when // the turn named one. // // It is a REPORT, not a question, and it is what a surface draws instead of // a model thinking: what follows is the harness's report as ordinary text // and then EventTurnDone, or EventError if the run failed. EventHarnessRun // EventHarnessStep is one step of a running sub-harness, the instant it // lands: Step is the walk's own trail entry (subharness.RunWatched) and ID is // the run it belongs to — the id EventHarnessRun carried. // // It is a REPORT and it is DISPLAY-ONLY. A run takes minutes, and between the // announcement and the report there was nothing on screen saying which part of // it was happening. Nothing here is recorded: the report that follows carries // the whole trail (subharness.RunCard), so a step kept in the transcript would // be the same news written down twice. EventHarnessStep // EventHarnessDesign says a turn asked for a sub-harness to be BUILT — "make // a harness for triaging flaky tests" — and the design has started // (harness_build.go). Text is the goal, less the words that asked for it; // Hint is "designing"; Model is what the design is thinking with. // // TASK NAMES THE NODE IT RUNS AS, and it is the only field on this kind a // surface can act on. A design is a task now (harness_task.go): it has an id // a person can say out loud, a room they can walk into, and a stop. So the // one line this event draws names it — "harness · designing X — task 4" — // and everything else about the design's life arrives on the task lane, not // this one. Only ID is filled. // // It is a REPORT and it does not hold the turn: the turn is already over when // it arrives, because designing takes a minute and a conversation held on one // is a conversation nobody can use. Exactly one of EventHarnessDesignDone or // an EventNotice saying why not follows it, on the standing lane // ([Agent.HarnessDesigns]) as well as on the turn's stream. EventHarnessDesign // EventHarnessProgress reports one live snapshot of a harness design call. // It is display-only: partial JSON and reasoning never enter the transcript. // Goal names the request; Phase is designing or reviewing; Attempt and // Attempts size the retry ladder. ThoughtTail is the recent reasoning, Hint // is the best meaning recovered from partial JSON, Bytes is content received, // and Stalled says no delta has arrived for ten seconds. EventHarnessProgress // EventHarnessDesignDone carries a finished design in Harness, with the id // the answer goes back through in ID, the name in Text and the description in // Hint. // // It is a QUESTION — the only one on this list that outlives the turn that // raised it. A surface draws the page (subharness.CardLines is the renderer // every surface shares) and answers through [Agent.ResolveHarness], the same // method an offer is answered with: TRUE SAVES IT into the registry, false // drops it. Nothing is written before that answer, and a surface that ignores // this kind saves nothing — which is the same posture EventHarnessOffer // keeps, one lane over. EventHarnessDesignDone // EventHarnessDesignRevising WITHDRAWS a design card the person asked to have // changed. ID is the design it is about and Text is the change, in the // person's own words as the design's thread passed them on (harness_task.go's // revise_design). // // IT EXISTS BECAUSE A QUESTION CAN BE OVERTAKEN BY A THIRD ANSWER. A design // card is one decision behind two doors — the card in the conversation and the // approval row in the design's own room — and there is a third thing a person // can do with a page, which is to say what is wrong with it. When they do, the // page that card is about stops existing, so the card has to come down: left // standing it would be a save key over a draft that has been replaced, and the // answer it took would save the wrong page. // // A surface takes the card back to the LIVE form it wore while the page was // first being written, because that is what is happening again — the designer // is at work, EventHarnessProgress starts arriving, and exactly one // EventHarnessDesignDone follows it with the rewritten page. The design's own // ROW needs nothing from this kind: the node moves back to the "designing" // phase on the task lane, and the approval row is drawn off that phase. EventHarnessDesignRevising // EventOrchestrateNote carries one planner note from an adaptive run // (internal/orchestrate): Text is the note, ID the run. A REPORT; the room // draws it as the thin thinking-row between completions. EventOrchestrateNote // EventOrchestrateFuel is the gauge and its early warning: Text is the // spend summary ("$1.60 of $2.00"), Hint holds the cap. A REPORT at the // 80% mark and whenever a surface asks; it never blocks anything. EventOrchestrateFuel // EventOrchestratePause says the run hit its fuel cap: in-flight nodes // finished, nothing new launched, the frontier is frozen mid-shape. Text // is the spend summary. It is a QUESTION answered through // [Agent.ResolveOrchestrate] — top up, finish with what we have, or stop — // and until that answer the run sits in its Paused state, resumable. EventOrchestratePause // EventToolFinished says ONE call's own work is over, the instant it is // over, and carries how long that call took in Took. // // It is a CLOCK EVENT and nothing else: the result is not in it, and the row // is not closed by it. The result still arrives as EventToolEnd or // EventToolFailed, after the whole batch has finished, in call order — the // order the transcript is written in. // // It exists because those two moments are not the same moment. A batch's // calls run together and finish in any order, so a `cd` that took five // milliseconds sat under a spinner and a climbing clock until the slowest // call beside it returned, and then claimed that whole span as its own // duration. The row was reading the BATCH's clock. This is the call's own, // measured where it ran (loop.go's executeTool), so a surface can stop the // row's clock and state the figure the call actually cost. // // A surface that ignores this kind is exactly what it was. EventToolFinished // EventRetrying says THIS STEP IS BEING ASKED AGAIN, and that whatever the // dead attempt streamed is void. Text carries the one line explaining why — // "nothing came back from the model — asking again", "the reply lost its // thread — that text was dropped, asking again". // // It fires when the stream guard cut a request (internal/provider's // streamguard.go): the endpoint went quiet, or the reply stopped being // language. It also fires when a transport failure is about to be retried. // A rescue taking over a visible answer on another machine serving the same // model fires it too (internal/provider's hedge.go). // The turn loop discards that attempt's partial text, its early reads and // its half-arrived calls before the next request, so A SURFACE MUST THROW // AWAY WHAT IT DREW FOR THEM TOO — everything after the last thing the person // typed belongs to a response that will never exist, and leaving it on screen // would show half a dead answer above the live one. // // AND IT FIRES WHEN THE STEP MOVES TO ANOTHER MODEL, which is the same news // about the same attempt and a different thing to draw: the rest of the reply // arrives in a different voice, at a different price. [Event.Retry] is what // tells the two apart — its Next names the model being moved to and is empty // on an ordinary retry (retrynews.go) — and Text carries the whole sentence // either way, so a surface that reads only Text is exactly as correct as it // has always been. // // It is also the one place a surface learns that a wait is a RETRY rather // than a first attempt, which is the difference between "waiting for" and // "trying again". It never ends a turn: either the next attempt streams, or // EventError arrives with the sentence about giving up. EventRetrying // EventStandingProposal asks the person whether one standing item — a // reminder, a watch, a rule, an overnight job — may stand (standing_contract.go). // Standing carries the card; the ID inside it is the token a surface hands back // to [Agent.ResolveStanding]. Nothing stands until the answer is yes. EventStandingProposal // EventStandingUpdate reports a standing item changing under a live window: it // was ratified, it fired, it was paused, retired, or it needs the person. It is // a report, never a question. EventStandingUpdate // EventSubharnessAsk is a running subharness putting one question to the // person (subharness_env.go, the Env's ask() door). ID is the run's task // node, Text is the question in the program's own words, and Args carries // the answers it offers as a JSON array when it offers a set. // // It is a QUESTION and it arrives IN THE RUN'S ROOM, which is where the run // lives: its journal, its progress and its ✕ are all there already, and a // question about the work belongs beside the work. A person who is not in the // room learns about it from the ROSTER, because the node moves to the // "awaiting your look" phase for exactly as long as the question stands — // the same phase a design waiting on its card wears, for the same reason. // // It is answered through [Agent.AnswerSubharness], which takes the run's id, // what they said, and whether they are TAKING OVER. A surface that ignores // this kind leaves the run waiting until the node is stopped or the session // closes, which is why the question is only ever put where somebody is // watching (Config.AskConsent) — an unattended run answers from what the gate // declared or stops incomplete, and never guesses. EventSubharnessAsk // EventSubharnessStep is one host call a running subharness just made // (internal/exec's JournalEntry): ID is the run's node and Step carries the // entry whole — which door, what it was about, what it cost. // // It is a REPORT and it is DISPLAY-ONLY, on EventHarnessStep's terms: the // journal is the permanent record, this is how a person watches it being // written. Nothing here is recorded in any transcript. EventSubharnessStep // EventSubharnessProposal asks whether one saved program should take this // piece of work (tools_subharness.go). ID is the token an answer goes back // through, Text is the program's name, Hint is what it is for, and // Subharness carries the INTAKE CARD — every input field, what this // conversation already answers, and which required blanks are left. // // It is a QUESTION and it is the one on this list with NO CLOCK THAT // APPROVES. A task proposal's countdown ends in a yes because it is a window // to redirect ordinary work; this one may not, because a program that ran // because nobody answered would be exactly the silent auto-execution the // whole path is built to prevent (docs/SUBHARNESS-PRD.md §9). It is answered // through [Agent.ResolveSubharness] — true runs it, with the form as the // person left it — and a surface that ignores this kind runs nothing at all, // which is the correct behaviour rather than a degradation. EventSubharnessProposal // EventSubharnessProposalOff takes the card named by ID back down. Nothing // ran, and nothing about the person's own intentions is being reported: the // tool call that raised the card has let the turn go, either because its // window expired or because the turn it belonged to was interrupted // (tools_subharness.go). // // IT EXISTS SO THAT A CARD CANNOT OUTLIVE ITS LISTENER. The window is a // bound on the TOOL CALL and not a deadline on a person, so it fires while // the card is still on somebody's screen — and a card left standing after it // would be a `run it` that resolves nothing, silently, which is the one // ending a question is never allowed to have. A surface that ignores this // kind leaves that dead card up; a surface that draws it takes the card down // and says so. EventSubharnessProposalOff // EventTaskReplyTags names the finished tasks whose notes the next words // answer. TaskReplyTags carries them in note order. EventTaskReplyTags // EventSteerAccepted says one sentence the person typed INTO the running // turn is on the queue and will reach the model at the next step boundary // (steer.go). Steer carries its identity, its words and the instant it was // sent; nothing is in the transcript yet. // // It is a PROMISE AND NOT AN OUTCOME, which is why exactly one of the two // kinds below always follows it on the same stream: the boundary it is // waiting for may never come. EventSteerAccepted // EventSteerConsumed says the model HAS BEEN GIVEN that sentence: it is in // the transcript as user content of the turn it was typed into, and the // request carrying it is the next thing that goes out. Steer names which // steer landed. EventSteerConsumed // EventSteerFellThrough says the turn ENDED FIRST — it answered, it faulted, // or somebody stopped it — with that sentence still waiting, so no request of // that turn ever carried it. The words are not lost and they did not steer // anything: they move to the queue that holds a message waiting for a turn of // its own, and the stream the steer was sent on carries that turn when it // starts (steer.go states the whole law). EventSteerFellThrough // EventTaskPhase says one running node has moved between its three lives — // its worker, the check that reads what the worker left, a repair round // closing the gaps the check named (task_audit.go). TaskPhase carries the // node's id, the word, the round numbers while a repair runs, and the // check's one-line finding. // // IT RIDES THE TASK LANE beside EventTaskUpdate — the turn's hub AND the // standing [Agent.TaskUpdates] subscription — because a check that takes // four minutes takes them long after the turn that proposed the work ended. // // IT IS NEWS AND NEVER A ROW. The node's state does not move: it was // running before the check and it is running after it, so a surface folds // this into the row an update already gave it and never opens one from it. // A surface that ignores this kind is what it was — which is what the // evidence in #76 §5 describes: minutes of check and repair drawn as // nothing at all, and a person concluding the work hung. EventTaskPhase // EventTakeover says another window on this machine has asked for this // conversation and the turn it was in has ended (takeover.go). It rides the // standing task lane and nothing else, and Text carries [TakeoverWord]. The // surface that hears it lets go — detaches and closes the conversation the // way /new does — and the window that asked resumes it from the checkpoint. EventTakeover // EventJobUpdate reports one BACKGROUND JOB's life (jobnotice.go): Job // carries the job's own id, the short name it has been given, the command, // the state, the log path and — once it is over — the exit code. It fires // when a job starts, when its name arrives, and when it settles. // // IT IS ITS OWN KIND BECAUSE A JOB IS ITS OWN THING. A job used to ride // EventTaskUpdate as a [TaskNotice], which left every surface downstream // carrying a clause saying a job is not really a task — no room, no branch, // no price, no stop, no card and no index row. What a job has is an id, a // log and an exit code, and none of those is what a task row is drawn from, // so it is published as what it is and the clauses go away. EventJobUpdate // EventCaption carries one line NAMING THE OPEN STEP (caption.go): the // cheap narrator asked shortly after the tools begin. Text is the step // title — a checklist item, not reasoning. It is news about the open step, // never a new block of its own — the surface keys it onto the caption // already drawn for that batch. // // A SURFACE THAT IGNORES THIS KIND IS UNCHANGED: the deterministic // composite already stands in the caption slot, and this event only // replaces that floor when a cheap model had something better to say. // // [Event.Category] rides with it and is the same news about the same step: // which FAMILY of work the sentence is about (actioncategory.go). It is // empty whenever the narrator did not name one, and a surface reads that // emptiness as "ask the tools", never as "draw nothing". EventCaption // EventAssistantDone marks the journal boundary for one valid, non-empty, // tool-free assistant response. Its content has already arrived through // EventTextDelta and has been recorded before this event is published. It // carries no prose of its own: a surface uses it to stop treating those // streamed words as provisional while the end-of-turn checks still run. // // IT IS APPENDED TO PRESERVE EVERY EXISTING WIRE NUMBER. Hosts serialize // EventKind as an integer, so inserting a kind above this point would make // an older binary read every later event as a different fact. EventAssistantDone // EventMoved says a WINDOW SOMEWHERE ELSE HAS OPENED THIS CONVERSATION and // is now the one in it. Text carries [MovedWord]. // // IT IS NOT [EventTakeover] AND THE DIFFERENCE IS WHAT HAPPENS TO THE WORK. // A takeover is asked for on this machine's disk and answered by a window // that OWNS the engine: it interrupts, closes, and the work lands paused for // the window that asked to resume. A move is announced by an engine that // holds the conversation itself (internal/enginehost) to every other surface // attached to it: nothing is interrupted and nothing pauses, because the // engine goes on running the turn while the surfaces around it change. The // window hearing this DETACHES — it does not close. // // IT RIDES THE STANDING TASK LANE for EventTakeover's reason exactly: it is // the one subscription that outlives every turn, and a move happens most // often in the middle of one. EventMoved // EventQuestion carries one whole [Question] in Question: a decision this // engine is handing to the person, with its evidence, its answers, the // asker's own pick, what is waiting on it and what an answer costs // (question.go). // // IT ARRIVES AFTER THE ROWS IT IS ABOUT, exactly as EventConsentRequest // already orders itself against its batch's EventToolBegin rows, and for // the same reason: a question attaches to a row a surface has already // drawn, and one that arrived first would be a question about nothing. // // IT IS A SECOND DESCRIPTION AND NEVER A REPLACEMENT. Every lane goes on // emitting the event it always emitted — EventConsentRequest, // EventTaskProposal, EventStandingProposal and the rest — so a surface that // ignores this kind is exactly what it was. A surface that draws it draws // one object for every lane instead of thirteen cards. // // IT RIDES THE TURN IT WAS RAISED IN, AND [Agent.WatchQuestions] BESIDE IT — // never the standing TASK lane, which is the roster's and whose readers walk // a strict sequence of rows. EventQuestion // EventQuestionWithdrawn says a question stopped being one: the subject // settled, the clock took it, the plan changed, another answer made it // moot. Question carries the same object with [Question.Withdrawn] filled // in, so a surface has the head it drew and the sentence to retire it with. // // A QUESTION IS NEVER SIMPLY GONE. A count that drops for no reason a // person can see is a count they stop believing, so the reason travels with // the withdrawal and is drawn once, dim. EventQuestionWithdrawn // EventQuestionAnswered carries the whole [Answer] in Answer: what was // picked, what was said beside it, who decided and how long it lasts. // // THIS ONE IS KEPT. It is written to the session's own decisions.jsonl as // it is emitted ([Agent.Decisions] reads it back), because it is the // DECISION RECORD — the first rung of the ladder, the thing an asker reads // before it puts anything to anybody. Consent is deliberately not journaled // (a question about work that has not happened yet); an ANSWER is the // opposite of that: it is the one thing about a question that stays true // afterwards. EventQuestionAnswered // EventRowNews carries one line in Text about A ROW THE PERSON WROTE that // this build has stopped acting on — a pinned machine the router refuses to // serve a model from, a base that will not carry a lane choice at all // (internal/provider's lanepin.go and prefcarry.go). // // IT IS NOT [EventNotice] AND THE DIFFERENCE IS WHO THE SENTENCE IS FOR. A // notice is the adapter saying what it did to a request to get it accepted, // and it is over once the answer lands — a surface may fold it away with // the rest of the machinery. This is the only account a person will get of // why the machine they named has stopped appearing, and there is nothing to // fold it into: it asks them to do something (pin again, or leave it on // auto). Measured on 2026-09-13, riding the wrong kind: the pin was // retired, `@deepseek` came off the model word, another machine answered, // and the chat's work chip had swallowed the sentence that said so. // // IT IS LAST IN THIS BLOCK AND EVERY NEW KIND BELONGS HERE, because a kind // is an integer on the remote wire (internal/remote's EventWire): one added // in the middle renumbers every kind under it, and a window and an engine // on two builds would then disagree about what each other's events were. EventRowNews // EventQuestionDiscussion carries a reply beside a pending decision. EventQuestionDiscussion // EventToolOutput carries literal stdout/stderr in Text while a user shell // command is running. CallID owns the bytes; its result still ends the call. EventToolOutput )
type Exchange ¶
type Exchange struct {
// Option names the answer this was about, and "" is a question about the
// question itself.
Option string `json:"option,omitempty"`
// Asked is the person's words; Replied is the asker's.
Asked string `json:"asked"`
Replied string `json:"replied,omitempty"`
At time.Time `json:"at,omitzero"`
}
Exchange is one round of asking back: a person's question about one of the answers, and the asker's reply. IT IS BOUNDED AT ONE PER ANSWER by design — a question that turns into a conversation is a conversation, and the box below is already open for one.
type Expectation ¶
type Expectation struct {
// Path is where to look, relative to the folder the work is about or
// absolute. It is required: an expectation with nowhere to look is one
// nothing can answer, and this file's header says why that is refused
// rather than carried.
Path string `json:"path"`
// Holds is text that must be findable at Path — a symbol, a heading, a
// column name, an identifier. Empty asks only that the path is there.
Holds string `json:"holds,omitempty"`
// Absent turns the expectation around: this path must NOT be there. It is
// how a divider says "the file you are thinking of is gone", which is
// exactly the sentence the run this was written from got wrong.
Absent bool `json:"absent,omitempty"`
// Fact is the assumption in the divider's own words, and it is optional
// because most expectations say themselves. When it is there it is what the
// report reads, because the person who wrote the brief can say why the
// thing matters and this file cannot.
Fact string `json:"fact,omitempty"`
}
Expectation is one thing a brief relies on being true of the world its worker will get.
type FactSource ¶
type FactSource interface {
Model() string
Title() string
Usage() Usage
ContextTokens() int
ReasoningLevels() map[string]string
}
FactSource is anything that can answer the five questions a Facts is made of. Agent satisfies it, and so does the slice of an agent an engine serves across a connection (internal/remote's WrappedAgent) — which is the point: ONE BUILDER fills a Facts, on either side of the seam, so the value a surface reads and the value a test builds cannot drift apart.
type Facts ¶
type Facts struct {
// Memory is whether this request profile can use its saved notes. A missing
// field identifies an older engine that only states it in the welcome;
// an explicit false must travel when a full profile becomes lean.
Memory *bool `json:"memory,omitempty"`
// NeedsPerson names an outstanding human decision, including hidden chats.
NeedsPerson bool `json:"needsPerson,omitempty"`
// Model is the model the next request will use ([Agent.Model]).
Model string `json:"model,omitempty"`
// Title is the name the session gave itself ([Agent.Title]), and empty for
// a conversation that has not earned one yet.
Title string `json:"title,omitempty"`
ShortTitle string `json:"shortTitle,omitempty"` // Deprecated: accepted for old records; never used as a name.
// Spent is the session's running total ([Agent.Usage]).
Spent Usage `json:"spent,omitzero"`
// ContextTokens is what the conversation weighs right now
// ([Agent.ContextTokens]).
ContextTokens int `json:"contextTokens,omitempty"`
// Reasoning is the level held for each model id anybody has dialled, keyed
// the way [ReasoningKey] folds an id.
//
// IT IS THE WHOLE MAP AND NOT THE ONE LEVEL IN USE, because the question a
// surface asks is per-model and not per-session: a picker draws a row for a
// model nobody has switched to and names the level waiting on it, and ctrl+t
// on that row reads the level back before cycling it. A photograph holding
// only the current model's level would leave both of those asking over the
// wire — which is the whole thing this type is here to stop.
//
// A model with no level set has NO ENTRY, never an empty one: the agent
// stores absence as absence, and so does this.
Reasoning map[string]string `json:"reasoning,omitempty"`
// Thinking is the RESOLVED rung this conversation's next turn will ask for
// ([Agent.ResolvedEffort]) — whichever scope decided it, and "" for a
// conversation asking for no thinking at all, which the emptiness law draws
// as nothing.
//
// IT IS THE RESOLVED RUNG AND NOT THE STORED ONE, because that is the word
// the dial on the seam draws (internal/tui3's effortchip.go states the law:
// what the cell says is what will happen). The stored rung is a question
// asked once, by a person opening the dial, and it goes over the wire as its
// own call rather than riding a photograph every frame reads.
//
// It is spelled `thinking` because that is the word every person-facing
// surface already uses for this setting — the settings row, the task clause,
// the dial itself — and a fact named one thing in the protocol and another on
// the screen is two vocabularies for one ladder.
Thinking string `json:"thinking,omitempty"`
// Approval is the RESOLVED posture this conversation's tool gate is
// standing at ([Agent.ResolvedApprovalPosture]) — ask, guardian, allow or
// deny, whichever scope decided it, and "" for a session with no gate. It
// rides the photograph for Thinking's reason: the approvals chip beside the
// rung is drawn on every frame (internal/tui3's approvalchip.go).
Approval string `json:"approval,omitempty"`
// Places is the folders this conversation is about, newest first
// ([Agent.Places]) — the person's own attachments among them, told apart by
// [PlaceRef.Arrival].
//
// IT RIDES THE PHOTOGRAPH BECAUSE THE FOLDER INDICATOR IS DRAWN ON A FRAME.
// The surface shows what is attached beside the composer and offers a key to
// remove one, so the set is asked for at repaint rate — and a reading that
// went to the engine would make the repaint rate of a terminal a function of
// a round trip, which is the exact defect internal/remote's replica.go
// exists to end. It moves once per deliberate act and is a handful of short
// strings, so it is the cheapest thing on this struct to state unasked.
//
// Nil is a conversation about nowhere else, which is nearly all of them.
Places []PlaceRef `json:"places,omitempty"`
// Skills is the names a person has put in front of this conversation by
// hand, in attachment order ([Agent.AttachedSkills]).
//
// IT RIDES THE PHOTOGRAPH FOR THE FOLDERS' REASON: the skill chip above the
// box is drawn on a frame, and the picker marks its rows from the same set
// after every toggle. It moves once per deliberate act and is a few short
// names. Nil is nothing attached, which is nearly every conversation.
Skills []string `json:"skills,omitempty"`
}
Facts is what a conversation says about itself in one value.
EVERY FIELD IS A FACT A FRAME DRAWS, and nothing else is here: the transcript is not (it is large, and it is asked for once), the workspace is not (the welcome carries it and it never changes), the standing items are not (they live on their own beat). The rule for adding a field is the one that put these five here — a frame or a keystroke reads it, so waiting on the wire for it is a terminal that has stopped repainting.
func FactsOf ¶
func FactsOf(source FactSource) Facts
FactsOf reads the whole set off one source.
THE FIVE READS ARE NOT ATOMIC WITH EACH OTHER, and they do not need to be: each field is taken under the agent's own lock, so every one of them is a value that was true, and the set is published again the next time any of them moves. A frame drawn from a photograph in which the usage is one instant newer than the title is a frame nobody can tell from a correct one; a lock held across all five would be the conversation waiting on a status line.
type FolderLanding ¶
type FolderLanding struct {
Folder string
Name string
// Files is what has been written, folder-relative, in write order.
Files []string
// Merged is the outcome once it has happened: [mergeMerged] for a branch
// that went home, [mergeConflicted] for one that would not and was kept,
// [mergeKept] for one deliberately left on a protected, moved or detached
// checkout, and [mergeInPlace] for a copy laid back by name. It is empty in a
// preview.
Merged string
// Note is the sentence that only exists when something needs explaining —
// a conflict, files nobody wrote, a copy that could not be laid back. The
// emptiness law: an ordinary landing says nothing here.
Note string
}
FolderLanding is what a landing did, or — before it is confirmed — what it would do. One shape for both, because the card that asks and the line that reports are looking at the same facts.
func (FolderLanding) Kept ¶
func (l FolderLanding) Kept() bool
Kept reports that the work did NOT go into the folder and is still where the landing found it — on its own branch after a merge git could not settle, or in the copy after a landing that could not save it at all ([cameHome]).
It is a method rather than a comparison a surface makes for itself, because the strings [taskTree.comeHome] answers with are this package's and a surface reading one of them by hand is the second copy of a fact that will drift.
func (FolderLanding) KeptByPolicy ¶
func (l FolderLanding) KeptByPolicy() bool
KeptByPolicy reports the ONE kind of keep that is not a failure: the landing worked exactly as designed and the branch was left alone because writing this checkout is something codeaf will not do.
IT EXISTS BECAUSE FolderLanding.Kept ANSWERS TWO DIFFERENT QUESTIONS. Every other keep is something that went wrong — a merge git could not settle, a landing that could not save the work at all — and a surface is right to phrase those as a failure. This one is a policy honoured, and phrasing it the same way tells somebody their work did not land when it did.
type GenerateImageArgs ¶ added in v0.4.0
type GenerateImageArgs struct {
Prompt string `json:"prompt"`
ReferencePaths []string `json:"reference_paths"`
AspectRatio string `json:"aspect_ratio"`
Size string `json:"size"`
Path string `json:"path"`
Model string `json:"model"`
}
GenerateImageArgs is the wire form, and the road's argument shape in one: the belt decodes into it, and the command line's image door fills it directly, so the two doors cannot disagree about what one call carries.
type GroundRung ¶
type GroundRung string
GroundRung names which rung of the ladder made one task's world. It is written onto the node so that a report can say what world the work was done in, which is the question nobody could answer about the run this file was written from.
const ( // GroundRungUniverse is a furrow fork of the whole workspace. GroundRungUniverse GroundRung = "universe" // GroundRungSnapshot is a machine commit of the parent's tree, with the // child's worktree carved from it. GroundRungSnapshot GroundRung = "snapshot" // GroundRungCopy is the folder copied file by file. GroundRungCopy GroundRung = "copy" // GroundRungHere is the honest nothing: the task works in the ground // itself, so it inherits the world by standing in it. GroundRungHere GroundRung = "here" )
type HarnessBeltSeams ¶
type HarnessBeltSeams struct {
// Media and MediaModel are [Config]'s own pair, with [Config]'s own meaning:
// the client that carries a generation request, and the resolver that says
// which model serves a modality. Either one absent takes every generation
// verb off, exactly as it does on the conversation's belt.
Media MediaGenerator
MediaModel func(modality string) string
// MediaPick is [Config.MediaPick]: the just-in-time model choice on the
// four making verbs. Nil keeps the `model` argument off their schemas,
// exactly as it does on the conversation's belt.
MediaPick func(modality, word string) (string, error)
// Seer is the completer view_image asks to look. It is separate from Media
// because looking is a CHAT call to a vision model and not a media-endpoint
// call, which is the same split the conversation's belt makes
// (tools_view.go). Nil leaves view_image off however well the resolver
// answers, because a verb with nothing to send the picture to is a verb that
// would refuse on every call.
Seer Completer
// Place and ArtifactsIndex are WHERE A HARNESS'S WORK LANDS AND HOW IT IS
// FOUND AGAIN — [Config]'s own two fields, with [Config]'s own meaning.
//
// They are seams rather than something this file could work out because a
// harness writes real deliverables and the media verbs decide where through
// exactly one ladder (landing.go's [ImagesDir] and its siblings): the owned
// workspace, then the borrowed session's own artifacts/, then the dot
// directory under the workspace. Without the Place every belt built here fell
// to that last rung — the one landing.go's essay calls the wrong answer about
// litter — so a film a saved procedure assembled landed in a hidden folder
// inside the person's repository, and without the index it appeared in
// `/files` nowhere at all. A harness is not a lesser writer than a
// conversation: what it makes is the same kind of file, wanted back the same
// way, and it lands in the same place.
Place Place
// ArtifactsIndex is the deliverables index file (artifacts.go). Empty
// records nothing, which is what a test and a door with no home both want,
// and it is not double-counting anything: a run's SPEND is accounted through
// the trail (see the throwaway agent below), while a row here is a citation
// of a path, written once by whichever hand actually wrote the file.
ArtifactsIndex string
}
HarnessBeltSeams is everything the media half of the belt needs from the door that builds it: what it may generate with, what it may look with, and where what it makes lands. It is a struct rather than a handful of arguments because the run door (cmd/codeaf's chatv3_harness.go) fills it from a config it already holds, and a positional list of two interfaces, two functions, a folder and a path is a call nobody can read at the call site.
ALL OF IT IS OPTIONAL, and it stays optional now that two of the seams are about WHERE rather than about what: a zero HarnessBeltSeams yields exactly the seven wire tools, which is what a harness has always had and what a machine with no media models still gets. A zero HarnessBeltSeams.Place does not take a verb away — it moves what the verbs write. Such a run lands its files on the legacy rung, `<workspace>/.codeaf/images` and its siblings (landing.go's ladder), and records none of them, which is the honest answer for a door that genuinely has no session folder to land in and the WRONG one for every door that has.
type Holder ¶
type Holder struct {
PID int
Build string
State PresenceState
UpdatedAt time.Time
// TTY is the holder's terminal, read off the process table when the
// holder is read, and "" when the machine will not say. It is what tells
// a person with six terminals open WHICH window this is.
TTY string
}
Holder is the process holding one conversation's journal, as its own presence.json last described it.
func ReadHolder ¶
ReadHolder is the holder of the conversation in sessionDir, from its presence file, WHETHER OR NOT THAT FILE IS FRESH. Freshness is the right test for "is this conversation alive" (the lock answers that); it is the wrong test here, because a wedged holder is exactly one whose heartbeat may have stopped while its flock is still held. A record older than TakeoverStale is refused all the same: a pid that old is too likely to have been reused.
type Image ¶
Image is one picture on its way into the conversation.
Path is where it lives and is what the journal records — a session file must stay readable, so it holds a REFERENCE and never the bytes (see [journalPart]). MIME may be empty, in which case it is read from the extension. Bytes may be nil, in which case the file is read from Path; a caller that already holds the bytes (a clipboard paste written to a temp file, a screenshot) passes them and the file is not read twice.
type ImageGen ¶ added in v0.4.0
type ImageGen struct {
Client MediaGenerator
DefaultModel string
Pick func(modality, word string) (string, error)
Workspace string
Directory string
Account func(model string, usage *ai.Usage)
Record func(path, prompt string)
}
ImageGen is everything one generation needs from the surface it runs in: the client, the default model its resolver answers, the picker for a call's own word, where unnamed pictures land, and the two hooks the belt adds — the bill and the artifacts row. Account and Record are optional: a caller with no session passes neither, and the picture still lands.
type InputKind ¶
type InputKind string
InputKind is what a person types, toggles or drags to answer, BESIDE the answers the asker wrote down. Free text is always available and is never the only door (the ladder's last two rungs).
const ( // InputNone is a question answered entirely by picking one of its answers. InputNone InputKind = "" // InputText is one free-text box. InputText InputKind = "text" // InputBlanks is a small form: [InputShape.Blanks], each with its own kind // and default. InputBlanks InputKind = "blanks" // InputChecklist is several answers at once rather than one, which is the // one kind whose answer list may run to eight. InputChecklist InputKind = "checklist" // InputPairs is this-or-this, once per row. InputPairs InputKind = "pairs" // InputDial is a number on a range, drawn as a dial and answered with the // arrows — and never drawn at all on the screen-reader tier, which gets a // number instead. InputDial InputKind = "dial" )
type InputShape ¶
type InputShape struct {
Kind InputKind `json:"kind,omitempty"`
// Blanks are the fields of an InputBlanks form, in the order they are
// drawn and tabbed through.
Blanks []Blank `json:"blanks,omitempty"`
// Dial is the range of an InputDial, and nil on every other kind.
Dial *Dial `json:"dial,omitempty"`
// Prompt is the one line above a free-text box, and "" draws nothing above
// it at all.
Prompt string `json:"prompt,omitempty"`
// Secret says the answer is a CREDENTIAL and is never drawn back. A surface
// that honours it masks the box a character at a time and shows how many
// characters arrived, which is what a person pasting a key needs to know and
// the whole of what they need to know. It is on the question rather than
// decided by each surface because the asker is the only thing that knows a
// key from a folder name, and a box drawn in the clear once is a secret on
// somebody's screen.
Secret bool `json:"secret,omitempty"`
}
InputShape is what a person may give BESIDES a pick. Its zero value is InputNone, which is the ordinary case: most questions are answered by pressing one of the keys the asker wrote down.
func TaskModelShape ¶
func TaskModelShape(notice TaskNotice) InputShape
TaskModelShape is the small form a proposal carries when — and only when — the harness raised a shortlist it could not choose within (TaskNotice.ModelOptions).
ONE OPTION IS NOT A CHOICE, so an ordinary proposal carries no shape at all and the card draws no hole: a row offering the one model the work was already going to run on is a question that has answered itself. That is the same bound the row of model chips this replaced kept, said once instead of in the renderer.
IT IS EXPORTED BECAUSE THE SURFACE BUILDS THE SAME QUESTION, for the reason TaskProposalLead states: two builders that drifted would put two questions on screen about one proposal.
type JobKind ¶
type JobKind string
JobKind is what sort of background work a job is, in the one vocabulary a surface reads. It is the registry's own [jobKind] made public.
IT EXISTS SO A SURFACE NEED NOT READ THE NAME TO KNOW THE KIND. A watch and a render behave differently on a page — a watch has ticks and no ending, a render has an artifact at the end of it — and a surface that had to infer that from a label reading "watch app" would be parsing prose for a fact the engine already holds.
const ( // JobKindCommand is a shell command running in the background: a server, a // build, a sweep. It is the ordinary case and the only kind whose name is // worth asking a model for, because it is the only one with no label of its // own (jobname.go). JobKindCommand JobKind = "command" // JobKindWatch is a command re-run on a timer, which ends when it is stopped // and not when it is finished. JobKindWatch JobKind = "watch" // JobKindRender is a provider call that makes a file — a video, a piece of // music — and is a job for as long as it renders. JobKindRender JobKind = "render" // JobKindTask is a task node's own worker. It is in the registry for the id // space, the log and the kill, and it is NEVER published as a job: the work // already has a roster row of its own, and a second row would be the same // piece of work counted twice. JobKindTask JobKind = "task" )
type JobNotice ¶
type JobNotice struct {
// ID is the job's own number, and it is the ONLY id it has. It is what
// `jobs output 3` and `jobs kill 3` take, what [Agent.Cancel] takes as
// `job:3`, and what a person reads on the row. One thing, one number.
ID int
// Name is the short name the job is CALLED — three or four words, given by
// the cheap namer for a plain command (jobname.go) or taken from the label
// the registry minted for a watch, a render or a hand.
//
// IT IS EMPTY UNTIL THERE IS ONE, and that is the emptiness law rather than a
// gap: naming is an errand on its own goroutine and the work never waits for
// it, so the first notice a job sends usually has no name on it at all. A
// surface draws Command until Name arrives and then redraws. It is never the
// id: `job 3` is the handle and is drawn beside the name, never as it.
Name string
// Command is what is actually running, whole and unshortened. A surface cuts
// it to whatever room it has; this is the truth it cuts from.
Command string
// Detail is a watch's terms — "every 10s on change" — and is empty for
// everything else.
Detail string
// Kind is what sort of background work this is.
Kind JobKind
// State is where the job is now.
State JobState
// LogPath is the file everything this job wrote is spooled to, absolute.
//
// IT IS THE WHOLE RECORD. A job has no transcript, no journal and no report,
// so when it is over this file is the only thing left to go and look at — and
// it outlives the process, so it is worth showing after the job has gone.
// A hosted session's path belongs to the ENGINE's machine and is not openable
// on the machine drawing it; that is the surface's fact to say, not this
// field's to hide.
LogPath string
// Started is when the process forked, and Elapsed is how long it ran — still
// counting for a live job, final for a settled one.
//
// BOTH ARE HERE BECAUSE A CLOCK THAT TICKS AND A DURATION THAT IS OVER ARE
// DIFFERENT DRAWINGS. A surface with only Elapsed would have to re-ask the
// engine four times a second to animate a running job's clock; with Started
// it counts up on its own beat and asks nothing.
Started time.Time
Elapsed time.Duration
// ExitCode is meaningful only when State is [JobDone] or [JobFailed]. A
// stopped job never reached one and a running job has not yet.
ExitCode int
// Ticks counts a watch's completed runs of its command, and is zero for
// everything else.
Ticks int
}
JobNotice is one background job, as everything outside this package sees it.
EVERY FIELD IS A FACT THE REGISTRY ALREADY HELD. Nothing here is derived, formatted or joined: a surface that wants `job 3 · 4m12s` builds that sentence itself, out of ID and Elapsed, in its own words and at its own width. That is the difference between this and what it replaces — the packed report was the engine choosing a surface's wording, and it fitted no width in particular.
func (JobNotice) Label ¶
Label is the job's name if it has one and its command if it does not.
IT IS ONE FUNCTION BECAUSE IT IS THE SAME CHOICE EVERYWHERE. A section row, a page header and a sentence dropped into somebody's message all want "what is this job called", all have to answer it before the namer has replied, and all have to answer it the same way — otherwise the row a person clicked and the page it opened are about two differently-named things. The width is the caller's business; this only picks WHICH string.
type JobState ¶
type JobState string
JobState is where a job is in its one and only life.
THERE ARE THREE AND THERE IS NO FOURTH. A job is running from the instant it forks — there is no queue in front of it and nothing admits it — and it ends either by finishing or by being stopped. Nothing audits a log file, so there is no unverified state here of the sort a task node has.
const ( // JobRunning is a live process, or a watch whose timer is still going. JobRunning JobState = "running" // JobDone is a command that exited zero. JobDone JobState = "done" // JobFailed is a command that exited anything else. JobFailed JobState = "failed" // JobStopped is a job somebody ENDED — `jobs kill`, the stop verb on its own // page, or the shutdown when the conversation closes. // // IT IS ITS OWN STATE RATHER THAN A FLAG ON FAILURE. "It failed" and "you // stopped it" are different news about a process that is equally not running, // and a surface that had to read a boolean beside the state to tell them apart // would be reading two fields to answer one question. JobStopped JobState = "stopped" )
type LandedTask ¶
type LandedTask struct {
Entry TaskIndexEntry
Session SessionRow
}
LandedTask is one piece of work that finished, with the conversation that ran it — the pair a "since you left" line is drawn from.
func LandedSince ¶
func LandedSince(w *World, since time.Time) []LandedTask
LandedSince is every task in the world that LANDED after since, newest first: a row whose landing instant is past the stamp and whose status is neither running nor queued.
IT READS NOTHING. The rows are the world's own (SessionRow.Tasks, one index read per bucket already paid for by ReadWorld), so this is a filter a surface may call on a draw.
EVERY LANDED ROW IS HERE, a task's parts and an adaptive run's workers included: TaskIndexEntry.Parent says which rows are, and a surface that wants one line per piece of work drops those itself rather than this deciding for every caller what counts as one.
A ZERO STAMP ANSWERS NOTHING, for ArtifactsSince's reason: a machine with no look yet has no origin, and the first look marks nothing as news (look.go).
type Landing ¶
type Landing struct {
ID uint64
Title string
State TaskState
Report string
Signature string
// Ending is WHY a failed landing stopped where it did, in the harness's own
// typed word ([TaskEnding]). It is here for one question: whether this
// failure is a finding about the WORK at all ([Landing.aboutTheWork]).
Ending TaskEnding
// Files is what this unit of work changed, worktree-relative and in the
// words its own ledger uses. It answers whether a failed unit's work exists
// on the tree anyway, having been done by somebody else
// ([Remains.absorbedBy]).
Files []string
// Merged says the work came home — not merely that the node said done, but
// that what it made is on the person's own branch. Only a merged landing may
// absorb a failed one, because a landing that finished and could not come
// home is not the tree holding anything.
Merged bool
// Retained names changed work kept outside this session's deliverable.
Retained string
InPlace bool
Delivered bool
Elsewhere bool
Produced bool
// Checked says this unit's OWN check read the work and accepted it. It is
// narrower than done on purpose — a unit taken as it stands, one landed with
// the check switched off and one a person accepted are all done and none of
// them was judged — and [Remains.absorbedBy] will not let a landing nobody
// judged speak for somebody else's work.
Checked bool
}
Landing is ONE UNIT OF WORK COMING HOME, in the fields a principal decides on. It is a reading of a task node and never the node itself: the interface must be answerable by something that has never seen this package's graph.
Signature IS WHAT MAKES TWO FAILURES THE SAME FAILURE. It is what the loop guard counts (Steward.Report), and it is supplied by the caller rather than derived here because what "the same failure" means belongs to whoever classified the landing — the sibling taxonomy lane owns that word, and a Landing carrying one it wrote is exactly the hand-off this field is for. An empty Signature is a landing the guard cannot count, and it is then never counted rather than lumped in with every other unsigned one.
type LaneNews ¶
type LaneNews struct {
// Model is the model the answer came back on. A news with no model belongs
// to nobody and is dropped.
Model string
// Lane is the machine the request WENT TO, and Winner the machine that
// finished it. They differ only when a rescue landed, which is the whole of
// what a surface means by "rescued".
Lane string
Alt string
Winner string
// TTFT is the wait before the first token and Rate how fast the answer was
// written, both as this session timed them on its own stream.
TTFT time.Duration
Rate float64
// Hedged says a second request went out for this answer; Trying says one is
// out RIGHT NOW and nobody has committed yet.
Hedged bool
Trying bool
// Reason is why the rescue went out, in the transport's own two words —
// [provider.RescueSlow] or [provider.RescueRefused]. Empty is a rescue
// nobody classified, which a surface reads as slow.
//
// AND ONE WORD THAT IS NOT ABOUT A RESCUE AT ALL. [provider.RescueRetired]
// is a person's own pin, refused by the router for this model and stood
// down until they pin again (issue #456). It rides this seam because it is
// the same KIND of sentence — something about the machine behind this
// answer changed while you were waiting — and the fact also reaches the
// conversation as a note of its own, which is the copy that stays.
//
// IT IS CARRIED AND NEVER DECIDED HERE. The word a person reads has to be
// the word the ledger acted on, and a second opinion formed at this seam is
// how a status line ends up disagreeing with the routing it is describing.
Reason string
// Failed WITHDRAWS a claim this seam already made: the lane named in Alt is
// the one a `trying X…` was about, and it has now failed. A surface that
// went on drawing the promise would be telling somebody about a request
// that is over.
Failed bool
// Role is who the answer was for (internal/lane's roles.go). A surface
// draws only the roles a person is reading: a naming errand and a memory
// reflex both answer during an ordinary talk turn, and a status line that
// took the lane and the rate from whichever of them finished last was
// telling somebody about a machine that had nothing to do with the answer
// they were waiting for.
Role lane.Role
At time.Time
// Session is the conversation this answer belongs to, and it is EMPTY IN
// EVERY BUILD THAT NEEDS NO ANSWER: one process with one window has nothing
// to disambiguate. An engine that is a separate process from its surfaces
// (internal/enginehost) reads it to decide which connection this sighting
// belongs on — see [Agent.newsKey].
Session string
// Subject is WHAT THIS SIGHTING IS ABOUT, and it is [provider.PhaseNews.Subject]
// under this seam's own name — the same spelling on both, because they are
// twins and a surface that had to remember which of them called it what is
// a surface that will key one desk differently from the other.
//
// EMPTY MEANS THE CONVERSATION, and that is the whole of the compatibility
// story: every producer that names no subject is talking about the
// conversation, so absence behaves exactly as it did before the field
// existed. A node's own sighting names the node ([Agent.newsSubject]), so
// its room can say which machine answered IT rather than showing whichever
// answer on the same model id landed last.
Subject string
// Relayed says this news arrived over a connection from the engine that
// produced it, rather than off this process's own stream. It is
// [provider.PhaseNews.Relayed]'s twin and exists for its reason: a build
// that is both serving and watching must not forward what it just received
// back out of the door it came in.
Relayed bool
}
LaneNews is one answer's lane story, as the layer that sent it knows it.
Anything unknown is left zero, and a zero field draws nothing — the emptiness law, said at the seam rather than at the surface, so that no reader has to invent a figure to have something to print.
type MediaGenerator ¶
type MediaGenerator interface {
GenerateImage(ctx context.Context, request provider.ImageRequest) (*provider.ImageResponse, error)
Speak(ctx context.Context, request provider.SpeechRequest) (*provider.SpeechResponse, error)
// GenerateMusic is the composing lane, and it is a METHOD OF ITS OWN rather
// than Speak with a music model in it. The two are not one endpoint wearing
// two hats: speech posts to /audio/speech, music has no media endpoint at
// all on the router and is composed through streaming chat completions
// asking for an audio modality back (internal/provider/music.go). A caller
// that sent a composition brief to Speak would get 404s from a lane that
// looks like it should work, which is what it did before this method
// existed.
GenerateMusic(ctx context.Context, request provider.MusicRequest) (*provider.MusicResponse, error)
GenerateVideo(ctx context.Context, request provider.VideoRequest) (*provider.VideoResponse, error)
// Transcribe is the senses wave's addition (tools_sense.go): audio in,
// words out, through /audio/transcriptions. It is ADDITIVE — every existing
// implementation of this interface is provider.MediaClient, which grew the
// method in the same change (internal/provider/transcribe.go), so nothing
// that satisfied this interface before stopped satisfying it. A test's
// scripted generator must now answer it; embedding MediaGenerator in the
// fake is the cheap way to keep that true through later additions.
Transcribe(ctx context.Context, request provider.TranscriptionRequest) (*provider.TranscriptionResponse, error)
}
MediaGenerator is every media endpoint the belt reaches: still images (with references for image-to-image), speech, the async video job — and, since the senses landed, transcription. A nil MediaGenerator keeps every generation tool off the belt.
THE NAME SAYS "GENERATOR" AND THE FOURTH METHOD PERCEIVES, which is worth one sentence. This is one client and not four, because it is one account, one key and one base URL, and the belt would learn nothing from a second interface that resolved to the same struct. The alternative — a MediaPerceiver beside it — buys a truer noun and costs every caller a second nil check for a client that is present or absent as a unit.
type MemoryLine ¶
type MemoryLine struct{ ID, Title, Text string }
MemoryLine is one remembered thing as a surface prints it.
type Meta ¶
type Meta struct {
// ID is the session's id — the same 16-hex id the transcript header
// carries, and the folder's name.
ID string `json:"id"`
// Title is what a picker row says. Empty until something names the
// session; an empty title marks a session the launch groom may reuse.
Title string `json:"title,omitempty"`
ShortTitle string `json:"shortTitle,omitempty"` // Deprecated: accepted for old records; never used as a name.
// Workspace is the REAL workspace path — the resolved git root for a
// borrowed session, the work/ directory for an owned one. The encoded
// bucket directory above the session folder is derived from it and is
// NOT an identity; this field is.
Workspace string `json:"workspace"`
// LaunchDir is where the person actually stood when the session opened —
// the repo subdirectory, or the temp dir whose presence marks the session
// as sweepable litter.
LaunchDir string `json:"launchDir,omitempty"`
// Owned marks a session that owns its workspace (work/).
Owned bool `json:"owned,omitempty"`
// Model is the conversation's model at last save, for the picker row.
Model string `json:"model,omitempty"`
// Build names the codeaf that most recently wrote this identity.
Build string `json:"build,omitempty"`
// Effort is the rung on the effort ladder this conversation was set to —
// how hard its turns ask the model to think (internal/effort). Empty is
// "nobody set one for this conversation", which is every session until
// somebody dials it, and it means the rung below decides instead: the work,
// the role, or the install's own `effort` row.
//
// IT IS HERE SO A DIAL SURVIVES A RESTART. The rung was a live field on the
// agent and only that, which made it the same defect the Model row above was
// written to fix: a person set it, worked in it, closed the terminal, and
// came back to a conversation that had quietly forgotten.
Effort string `json:"effort,omitempty"`
// Approval is the posture this conversation set on its own tool gate —
// ask, guardian, allow, deny, or auto for "the settings rows decide"
// (approvalposture.go). Empty is "nobody moved it here", which is every
// session until somebody does, and it is here for the reason Effort is:
// a gate a person opened from inside a conversation must be open when they
// come back to it.
Approval string `json:"approval,omitempty"`
// Created is when the session was minted.
Created time.Time `json:"created"`
// LastUserAt is when the PERSON last said something. Resume order is on
// this and deliberately not on file mtime: a background write touching a
// file is not a person returning to a conversation
// (internal/store/session_rooms.go holds the original of this law).
LastUserAt time.Time `json:"lastUserAt,omitempty"`
// SpentUSD and Tokens are WHAT THE TALKING HAS COST — this conversation's
// own running total, every turn and every auxiliary call beside it, as
// [Usage] holds it while the session is open.
//
// THEY ARE HERE SO THAT A READER CAN ANSWER WITHOUT OPENING THE JOURNAL.
// The transcript's usage lines are the record and stay the record; a
// session's price is recoverable from them and nothing else. But a surface
// asking about every conversation on the machine (world.go) reads a folder,
// a meta.json and a presence file per session and must not grow a transcript
// scan per row — so the running total is stamped here at the end of every
// turn (placemeta.go's [Agent.stampSpend]) and read from here.
//
// Zero is "nobody has said", exactly as an absent Title is, and every
// surface draws nothing for it rather than $0.00 (the emptiness law).
// Tokens is input plus output as ONE sum, which is the spelling
// [TaskIndexEntry.Tokens] already uses for the same fact about a task.
SpentUSD float64 `json:"spentUsd,omitempty"`
Tokens int `json:"tokens,omitempty"`
// Places is the set of folders THIS CONVERSATION IS ABOUT beyond the one it
// is standing in — named by the person, or kept from a ground the work
// resolved (places.go). It is here for the reason Effort is: a set that
// lived only on the running agent would be a conversation that forgot every
// folder it was about the moment the terminal closed, and the whole value of
// it is that nobody is asked the same question twice.
//
// AN ABSENT FIELD IS A CONVERSATION WITH NO REFERRED PLACES, which is every
// conversation written before this existed and every one that has not
// accrued one yet. Like everything else in this file it is a citation: what
// the conversation touched is in the transcript, and this is the answer
// already worked out from it.
Places []PlaceRef `json:"places,omitempty"`
// Trees is the working copies this conversation holds of those folders, and
// what has been written into each that the folder itself does not have yet
// (standingtree.go).
//
// IT IS THE ONE THING IN THIS FILE THAT IS NOT A CITATION. Everything else
// here is recoverable by looking again — the workspace, the model, what the
// talking cost. Unlanded work is recoverable from nowhere, so this is
// written the moment it changes and read back at open, and a conversation
// closed with changes waiting comes back still holding them.
//
// An absent field is a conversation that has written nothing outside the
// folder it stands in, which is every conversation until one does.
Trees []StandingTree `json:"trees,omitempty"`
// Archived marks a conversation somebody PUT AWAY from home's resting
// list: it leaves its project's block and gathers under home's one folded
// archive line, reachable there and still found by search. It is the
// person's own act (home's `e`) and its own undoing — nothing automatic
// ever sets or clears it, and nothing else about the session changes.
Archived bool `json:"archived,omitempty"`
// ArchivedTasks hides individual task rows without changing their execution
// or putting away the conversation that owns them. IDs are local to this session.
ArchivedTasks map[string]bool `json:"archivedTasks,omitempty"`
}
Meta is one session's identity, written where a picker can read it without parsing a journal. It is a citation, not a copy: every conversation fact in it is recoverable from the transcript, while Build names the codeaf that wrote the citation. A session whose meta.json is missing or corrupt is a session with a blank row, never a session that will not open.
type ModelLanding ¶
type ModelLanding string
ModelLanding is WHEN a model a person just named reaches the work, and it is the only thing a surface has to know to say something true about the pick.
const ( // ModelLandsNow is the request in flight let go of, because nothing of it had // reached the person, and the same step asking again on the new model. ModelLandsNow ModelLanding = "now" // ModelLandsNextRequest is the answer already arriving being allowed to // finish, with everything the work asks for after it on the new model. It is // also what a pick lands as when no request is out at all. ModelLandsNextRequest ModelLanding = "next-request" )
type ModelSpend ¶
type ModelSpend struct {
// Model is the provider's own id, and empty where a line named none. A page
// makes the pretty name; this is the join key.
Model string
Calls int
Tokens int
USD float64
}
ModelSpend is one row of "what ran it": a model, and what it cost.
func UsageByModel ¶
func UsageByModel(lines []UsageLine) []ModelSpend
UsageByModel groups by the MODEL, dearest first.
THE MODEL ALONE, AND NOT THE PAIR IT USED TO BE. This grouped by (model, role) — the role being the auxiliary word a call gave itself, "title", "taskname" — on the argument that a page could then say which slice of a model's bill was naming. SCREEN 2c asks a different question and says so in its own caption: `by the model, and the role it was bound to`. The role a person can act on is the CREW BINDING, which is a fact about the settings and not about a call, so the page joins each of these rows against config.ModelSlots and this file answers one row per model. The per-call word is still on every UsageLine.Role for anything that wants it.
Ties break on calls, then on the name, so two runs over one ledger draw the same table in the same order.
type NothingToCompact ¶
type NothingToCompact struct{ Why string }
NothingToCompact is ErrNothingToCompact with the reason a person can act on: how little older conversation there was, or why the summary that would have shortened it did not land. errors.Is still matches the sentinel.
func (*NothingToCompact) Error ¶
func (e *NothingToCompact) Error() string
func (*NothingToCompact) Is ¶
func (e *NothingToCompact) Is(target error) bool
type OtherProject ¶
type OtherProject struct {
// Name is world.go's own naming of a bucket ([projectName]) and Path the
// workspace its sessions recorded — "" when none of them said, which a
// surface draws as nothing rather than as a guess.
Name string
Path string
// Tasks are the running nodes, the newest-spoken conversation first.
Tasks []OtherProjectTask
}
OtherProject is one project on this machine with live work in it: what to call it, where it is, and the nodes its live conversations have out.
func ReadOtherProjects ¶
func ReadOtherProjects(root, skip string, now time.Time, pid int) []OtherProject
ReadOtherProjects is every project under a places root EXCEPT one, with what each one's live conversations have out right now. A project with nothing running is not in the answer at all: a heading over no rows says nothing, and under the emptiness law a quiet project is quiet rather than "0 running".
THE READING AND THE LIVENESS RULE ARE WORLD.GO'S AND ARE NOT RESTATED HERE. [readWorld] is the one reader of the whole machine, SessionRow.Live is the one judgement of whether a conversation's claim about itself is still worth believing, and [projectName] is the one naming of a bucket. A second copy of any of the three here would be the place the machine-wide answer and the home page came to disagree about the same window.
skip is the caller's own bucket directory, already answered for above by Elsewhere; pid is the reading process, for OtherProjectTask.Mine.
type OtherProjectTask ¶
type OtherProjectTask struct {
ElsewhereTask
// Mine reports that the process reading this is the very process holding
// that conversation — a session the keeper has open behind this one, in
// another project.
//
// IT IS THE PID, AND THE PID IS NOT A LIVENESS TEST. taskpresence.go is
// emphatic that a presence file's pid must never decide whether a session
// is alive: pids are reused, and a state directory shared between two
// machines makes the number meaningless. This is the other question. Asked
// as "is this number MY number", a reused pid on another machine cannot
// answer yes to a process that is not running, and the worst a collision
// could do is call a stranger's window `open here` — while the alternative
// is telling somebody to go to a window they are already sitting in, which
// is the refusal [ReadElsewhere] exists to stop this build making.
// Liveness is still [SessionRow.Live]'s, decided before this is read.
Mine bool
}
OtherProjectTask is one running node in a project that is NOT this one.
It carries ElsewhereTask whole rather than restating its three fields, because a row from another project is the same fact about a further-away window and the emptiness law on the window's name is already written there.
type PendingDecision ¶
type PendingDecision struct {
// Notice is the node as every other surface already reads it.
Notice TaskNotice
// Journal is where the node's transcript is, as a URI a surface can open. It
// is the one thing a notice does not carry and the one thing somebody being
// asked to decide almost always wants.
Journal string
// Depth is how far down the family this node sits — 0 for a root. It is here
// so a surface can FOLD a decision under its family head without having to
// walk the graph itself, and never so that one can be hidden.
Depth int
}
PendingDecision is one node waiting on somebody to say whether its work holds.
IT IS NOT Decision, and the two are spelled apart on purpose: that one is the principal's answer about what a session should do next, and this is a question put to a person about work that has already happened.
It carries the node's whole notice rather than a handful of fields lifted out of it, because every surface that renders a decision already knows how to draw a notice — the card, the roster row, the room's foot — and a second shape would be a second thing to keep in step with the first.
func (PendingDecision) Waiting ¶
func (d PendingDecision) Waiting() string
Waiting names the person's own words for what this node is doing: it finished, and it needs a look. It is a method rather than a field because there is one spelling of it in this package ([taskUnverifiedNews]) and a copy on a struct would be the second.
type PendingRunRecord ¶
type PendingRunRecord struct {
Brief string `json:"brief"`
Ground string `json:"ground"`
Mode TaskMode `json:"mode"`
Asked []string `json:"asked,omitempty"`
}
PendingRunRecord is the accepted work needed to rejoin machine admission after restart. It is cleared once the working copy is recorded, before workers start.
type Person ¶
type Person struct {
// contains filtered or unexported fields
}
Person is the principal of an attended session, and IT ADDS NOTHING.
Every method answers what the engine answered before this interface existed, and the emptiness is load-bearing rather than a stub waiting to be filled: a person holds their own acceptance, spends against their own judgement, reads a landing and decides what to do about it themselves, and carries a stopped turn on by typing. The one thing it holds is the ask, because Principal.Ask has to answer something and the session already knows it — and the floor under carrying on, which is not an addition either: it is the same law Steward has always had, on the road that never got it.
func (*Person) Acceptance ¶
Acceptance is empty for a person, and that is the answer rather than a gap: somebody watching the work does not need the done-condition written down for them, and writing one on their behalf would be the harness deciding what they meant.
func (*Person) Budget ¶
Budget is unset for a person: an attended session has always run until they stopped it, and the spend rail (rail.go) is the ceiling they can already set.
func (*Person) Decide ¶
Decide is [Agent.readRemains]'s own rule with one law added: a reader with something to say re-opens the turn on it, silence ends the turn, AND A READER THAT SAYS WHAT IT SAID LAST TIME STOPS IT ([standstillFloor]). Nothing else a person could be shown is consulted, because nothing else was.
THE ECHO IS THE ADDED LAW AND #888 IS WHY. Measured 2026-09-11: a reply had reported that `zeta.txt` did not exist, and the reader re-opened the turn three times running with the same observation — that the missing file had not been reported — until the per-turn ceiling stopped the fourth. The model spent three turns explaining that the observation was mistaken, and the person read all three, because a carry-on is recorded as a user line and its answer as an ordinary reply. A second identical reading is not a second piece of evidence; it is the first one said twice, and this road was the only one on which that was allowed to buy another turn.
type Phase ¶
Phase is the word for what is happening, and it is provider.Phase under this package's own name for PhaseNews's reason: ONE VOCABULARY, and a reader that has to import two packages to name a phase and the news carrying it is two spellings waiting to happen.
type PhaseNews ¶
PhaseNews is one moment of one turn's life. It is provider.PhaseNews under this package's own name, so a surface imports one package rather than two.
type Pick ¶
type Pick struct {
// Key names one of the question's own answers, and the gate refuses a pick
// that names a key nobody offered.
Key string `json:"key"`
// Reason is one dim sentence: why this one. It is the half a person
// actually reads before pressing enter.
Reason string `json:"reason,omitempty"`
// Confidence is how sure the asker is.
Confidence Confidence `json:"confidence,omitempty"`
// WouldChange is WHAT WOULD CHANGE THE ASKER'S MIND — "if the file is
// generated, the other answer" — and it is the most useful line on a card,
// because it tells a person which fact they hold that the asker does not.
WouldChange string `json:"wouldChange,omitempty"`
}
Pick is the asker's own answer to its own question, and it is a POINTER on Question so that "I have no pick" is spelled once. A question with no pick draws no `enter →` line at all, because there is nothing for enter to take (the emptiness law).
func (*Pick) UnmarshalJSON ¶
UnmarshalJSON lets a pick be written as nothing but its key. A model asked for `"pick":{"key":"1"}` has sent `"pick":1` and `"pick":"1"` (deepseek-v4-flash, 2026-09-10), and both say exactly one thing: the first answer. A type that decodes itself decides its own grammar (toolargs.go), so the bare forms are read here; the object form goes back through the one decoder, so that what is inside it is read, and refused, by the same rules and in the same words as every other argument on the belt.
type Place ¶
type Place struct {
// Dir is the session folder. Empty is the legacy flat layout, and every
// path method on such a Place answers "".
Dir string
// Workspace is the tools root: the borrowed project root, or [Place.Work]
// when the session owns its workspace. It is recorded here as well as in
// meta.json because the running process asks constantly and the file is
// for the next process.
Workspace string
// Owned marks a session whose workspace is its own work/ directory —
// opened outside any project, with nothing borrowed and nothing littered.
Owned bool
}
Place names every location one v3 session may touch on disk.
func (Place) Artifacts ¶
Artifacts holds deliverables that have no natural home in the workspace — a generated image in a borrowed session lands here rather than littering the person's repo, and its row in the global index is how it is found. An owned session's deliverables land in work/ instead; this directory is the borrowed session's answer.
func (Place) Card ¶
Card is the state card: what the work is FOR and where it stands, maintained by the post-turn extractor and rendered into every system prompt (card.go). It sits beside state.json rather than inside it because the two are written by different hands — the model's own `track`/`commit` tools fill state.json, nobody's tool fills this — and a file one of them corrupted would cost the other its record.
func (Place) ID ¶
ID is the session's id, which is the folder's own name — the same 16-hex id the transcript header carries and Meta.ID records. It is arithmetic on Place.Dir like every other method here: the name IS the identity, so a caller holding a folder never has to open a file to learn which session it is. The legacy zero Place answers "".
func (Place) Logs ¶
Logs holds the droppings — job logs and stubbed tool results. Everything in it is re-creatable and carries the sweep's 7-day TTL; nothing in it is a deliverable.
func (Place) NodeJournals ¶
NodeJournals is where task nodes and their audits keep their transcripts — beside the conversation that commissioned them, not in a parallel tree.
func (Place) Transcript ¶
Transcript is the journal, and the flock that guards the session lives on it.
func (Place) Trees ¶
Trees holds the git worktrees, one per running node. Session deletion runs git worktree remove/prune against Meta.Workspace BEFORE this directory goes, or the repository is left holding registrations for paths that are gone. It uses the same canonical spelling git records even before trees/ itself exists, so creation, checkpoints and later unregistering all name one directory.
type PlaceArrival ¶
type PlaceArrival string
PlaceArrival is HOW a referred place got onto the conversation, and the only thing that turns on it is whether the place may go stale.
const ( // PlaceSaid is the person's own act: the picker, a path in their words, a // folder dropped on the window, `/attach` on a directory. A place somebody // NAMED never silently expires — said is never overruled, which is the law // taskstands.go already keeps about the rung it feeds. PlaceSaid PlaceArrival = "said" // PlaceKept is a ground the ladder resolved and this conversation wrote // down. It is a cache of an answer rather than an instruction, so it decays // by recency in the ranking below — though its record stays on the meta as // history, because what the conversation was about last week is not a lie. PlaceKept PlaceArrival = "kept" )
type PlaceRef ¶
type PlaceRef struct {
// Path is the absolute, canonical directory — the repository ROOT when the
// place is inside one, because a ground is cut from a repository and not
// from a directory inside it ([groundRoot] makes that same snap).
Path string `json:"path"`
// Arrival is which of the two roads above brought it.
Arrival PlaceArrival `json:"arrival"`
// Referred is when the conversation last named or resolved this place. It
// is what "lately" means in the ranking, and the set is held newest first
// so that the answer is usually the head.
Referred time.Time `json:"referred,omitempty"`
// Mode is the person's own word about how work happens HERE — today only
// "in place", meaning the work happens in the folder itself with nothing
// isolating it ([TaskModeInPlace]). It is NEVER GUESSED: absent means the
// mode falls out of the deliverable, exactly as [groundMode] decides it for
// every other ground.
Mode string `json:"mode,omitempty"`
// Chose is the directory THE PERSON ACTUALLY POINTED AT, and it is set only
// when the snap above moved it — so it is empty for every place chosen at its
// own root, which is most of them.
//
// THE SNAP IS REAL AND MUST NOT BE SILENT. A branch is cut from a repository
// and not from a directory inside it, so a person who picks
// `…/internal/session` gains the project; but a record that kept only the
// project would have quietly widened what they said, and neither the surface
// nor the model could tell them what actually happened. This is the one field
// that keeps the two honest with each other.
Chose string `json:"chose,omitempty"`
// Repository records whether git knew this directory when it was referred.
// It is the cheap fact a picker draws and a caller reads without paying for
// a `rev-parse` per row; the ladder asks git itself where it must be right.
Repository bool `json:"repository,omitempty"`
}
PlaceRef is one referred place: where it is, how it got here, when it was last referred to, what the person said about working in it, and the one thing the machine learned cheaply on the way in.
IT IS SMALL AND IT IS A CITATION. Everything in it is either the person's own act or recoverable by looking at the disk again, which is why meta.json can carry it (place.go's Meta) and why a field nobody wrote is simply absent.
type PlaceSource ¶
type PlaceSource interface {
Places() []PlaceRef
}
PlaceSource is the slice of an agent that knows which folders a conversation is about. Agent satisfies it, and so does the slice an engine serves across a connection — which is the point: one photograph carries the set, so a surface drawing the folder indicator never asks over a wire.
type PlanAgent ¶ added in v0.4.0
type PlanAgent interface {
PlanTasks() []PlanTaskRow
PlanTaskPage(id string) (PlanTaskPage, bool)
PlanNote(id, text string) error
PlanPause(id string) error
PlanResume(id string) error
PlanCancel(id string) error
PlanAmend(id, text string) error
PlanPriority(id string, priority int) error
PlanRunSummary(rootID string) (RunPlanSummary, bool)
RefreshRunSummary(ctx context.Context, rootID string, lastLook time.Time) (RunPlanSummary, bool)
}
PlanAgent is the complete optional plan capability a conversation surface may use. It lives in session so the local engine and remote client implement one method set rather than allowing each transport or surface to redefine it.
type PlanCommandPart ¶ added in v0.4.0
type PlanCommandPart struct {
Command string
Separator string
Start int
End int
SepEnd int
RecordAddressed bool
RunCopyPrefix bool
}
PlanCommandPart is one quote-aware command part and the facts only the session can establish about it. Separator is the text that followed it.
Command[Start:End] of the step's recorded command is this part as it was typed and [End:SepEnd] is the boundary after it, so a surface that leaves a part out CUTS ITS SPAN FROM THE RECORDED LINE and never retypes what it keeps. A joined-up copy of trimmed parts is a different line from the one that ran: it lost the spaces around every boundary and the bracket that closed the last one.
A FACT IS SET ONLY WHERE CUTTING IS SAFE. Both facts mean "this part is not the work", and both are set only on a part that stands in sequence with its neighbours: after the start of the line or a boundary that ends a command, and before the end of the line or another such boundary. A part inside a substitution or a group is never marked, because the line around a hole in one of those is not a command anybody ran. A pipeline is one command: it is marked whole when its first part is addressed to the record, and not at all otherwise.
type PlanProgram ¶
type PlanProgram struct {
// Name is the program's own name, the word its command is spelled with. It
// is empty only for a log with no record beside it, which is a program that
// never said who it was; the page draws no name it was not given.
Name string
// Stages is every stage the program said, in its hello, it would move
// through, in order. Empty when the program named none.
Stages []string
// Turns is the conversation: the newest [planProgramTurns] calls the
// program made, in the order they started, each as its latest record says.
// EVERY TEXT IN IT IS CUT TO ITS HEAD — the first line that says anything, at
// most [planProgramHead] bytes — and the run's own copy is taken out of it
// ([planRunCopies.strip]), because that is all the page draws. The record on
// disk is untouched: this is a reading of it.
Turns []delegate.Turn
// Earlier is how many calls came before the first of Turns, which the page
// says rather than draws. Zero when the page carries the whole conversation.
Earlier int
// Calls is how many calls reached a model: every call the log holds that
// codeaf did not refuse, the one in flight included. It is counted over the
// whole log and not over Turns, so it stays the run's own figure however
// long the run has gone.
Calls int
// CeilingUSD is the dollar ceiling the run handed the program, off the
// program record the worker writes at the hello (delegate.ProgramRecord),
// and zero when the run set none or the program has not said hello yet —
// which the page draws as no ceiling at all rather than as $0.00.
CeilingUSD float64
// Models and Effort are the models the program said it works on and the
// rung it asks them to think at (delegate.ProgramRecord.Models), which the
// task's page names so a person can see what the run was launched on. Both
// are empty until the program says.
Models []string
Effort string
// Listening says the program reads the messages its page's box and the
// conversation's `say` send it (delegate's inbox.go), and InboxClosed is why
// it stopped — senior-dev once it has handed in. The page's box offers to
// send words only while it listens.
Listening bool
InboxClosed string
// Listens is the declaration's capability, so a queued or starting page
// can distinguish a future listener from a program that reads no messages.
Listens bool
// Started says the record carries a process start, so a hello without
// message support is not mistaken for a program still starting.
Started bool
// Actions is what the program did: the newest [planProgramActions] lines of
// its action log, in the order they arrived, each as the program's own
// vocabulary reads it (delegate.Delegate.Reader) — the step of its process
// it served, the words, how it came out — and the lines it leaves out
// absent. Every text is cut to its head and the run's own copy is taken out
// of it, as the turns' are. Empty for a run from before the log existed,
// whose page is drawn from its turns.
Actions []delegate.Shown
// EarlierActions is how many actions came before the first of Actions,
// which the page says rather than draws.
EarlierActions int
}
PlanProgram is the program a task was handed to, as the task's page reads it: its name and stages off the program record, and its conversation with codeaf off the log the model API writes. A page carries one only for a task whose record folder holds either — which is to say only for a program's task — and every other page carries nil.
type PlanSpendLine ¶ added in v0.4.0
PlanSpendLine is one seat's share of a run's spending: the role the store gave the work, the model that seat spent most of its money through, and what the seat came to over the window it was asked about.
IT CARRIES THE MODEL BESIDE THE SEAT because the spend page draws the two on one line: a seat is a role, and the model is the thing a person can go and change when they read that a seat has grown dear.
type PlanStep ¶ added in v0.4.0
type PlanStep struct {
Kind string `json:"kind"`
Step int `json:"step"`
Command string `json:"command"`
Observation string `json:"observation,omitempty"`
FullOutput string `json:"full_output,omitempty"`
Writes []string `json:"writes,omitempty"`
Children []string `json:"children,omitempty"`
// NotRun is the engine's fact that the harness answered this call itself and
// nothing ran. Its absence is false, so a record written before the field
// draws as it did.
NotRun bool `json:"not_run,omitempty"`
// Refused is the engine's fact that the call was an action the worker
// attempted and a door refused. A NotRun step without it is a correction
// about the form of a reply, which is no step a person reads.
Refused bool `json:"refused,omitempty"`
// Parts are display facts derived from Command. Command remains the byte-for-byte
// record; a surface filters parts instead of rewriting that record.
Parts []PlanCommandPart `json:"parts,omitempty"`
// ObservationHeadWithheld says the head of what came back may not be drawn
// under this step's row, because the row leaves out a part that could have
// written it ([planStepDisplayFacts] states the law). IT IS THE NEGATIVE ON
// PURPOSE: its absence is false, so a step from an engine built before the
// field, and a step no display facts were made for, draws its head as it did.
ObservationHeadWithheld bool `json:"observation_head_withheld,omitempty"`
}
PlanStep is one line of a task's trajectory — one command the worker ran and the head of what came back. IT MIRRORS internal/run's Step, which is where the record is written: this package cannot import that one, because it imports this one, so the step line is decoded here from the same JSON shape. The two must move together, and the run engine's own file is the definition.
type PlanTaskNote ¶ added in v0.4.0
PlanTaskNote is one note on a task's page: what was said, who said it, and when. The author is the worker's agent name, empty for a person's note, which PlanTaskNote.Person marks so a surface can draw the two voices apart.
type PlanTaskPage ¶ added in v0.4.0
type PlanTaskPage struct {
Row PlanTaskRow
Description string
Result string
Checks []string
Folder string
Notes []PlanTaskNote
Steps []PlanStep
// Live is the step the task is running right now — the same reading
// [PlanTaskRow.Live] carries, lifted onto the page so a surface can draw the
// step ONE STEP EARLY, before its end line reaches the trajectory. The zero
// value is the honest answer for a task that is running nothing, and
// [plandb.LiveStep.Empty] is the one question a surface asks before it draws
// the line (the emptiness law, as the live step's own file states it).
Live plandb.LiveStep
// Children is the task's own children — the rows whose Parent is this task —
// in store order, so a page can draw the tree under the task the way the
// plan list draws it. Empty for a leaf, which is the ordinary case.
Children []PlanTaskRow
// WaitRows feed the page's two-way waits reading: own dependencies first,
// then open tasks directly waiting on this task. Empty omits the section.
WaitRows []PlanTaskRow
// Program is the program this task was handed to and the conversation it
// has had with codeaf so far (plandb_program.go): nil for every task a worker
// of this conversation's own drives, which is every page but a program's.
// A page that carries one is drawn as that conversation rather than as a
// list of steps.
Program *PlanProgram
}
PlanTaskPage is everything a person reads when they open one task: its row, the description that is its work order, every note left on it, and the steps its worker recorded.
type PlanTaskRow ¶ added in v0.4.0
type PlanTaskRow struct {
// Done, Running, Queued, Failed and Total summarize every task below a run root.
// They stay zero on ordinary task rows. Claimed work is running; pending and
// ready work is queued. Total includes every descendant store row.
Done int
Running int
Queued int
Failed int
Total int
ID string
Title string
Status string
// Hold is why a ready part or an unstarted root has no worker yet. It
// crosses the remote wire only while admission refused this row's start.
Hold string `json:",omitempty"`
// Stopped is true only when this task's own ending records a person's stop.
// It is established from store data here and crosses remote reads as row data.
Stopped bool
// Interrupted is true only when this task was ended because nothing was
// driving its run when the next request arrived ([planTaskInterrupted]). It
// crosses remote reads as row data, the way Stopped does.
Interrupted bool
Seat string
Parent string
// Depth is the row's level below the page task; direct children are zero.
Depth int
// Waits is the tasks this row is held behind that are not its parent: the ids
// of its hard dependencies (feeds_into/blocks), in store order, and empty
// when it waits on nothing but its own parent. A row still `pending` because
// of one of these hangs under it and names it ([planWaits]).
Waits []string
// Steps is the count of the task's own trajectory lines — the steps its
// worker recorded, which is what the row's "14 steps" counts.
Steps int
// USD is the sum of the task's spend rows: what this piece of the plan has
// cost so far.
USD float64
// Model is the model this task's own spend rows spent most through, and
// Tokens the tokens those rows carried, in and out together. Both are read
// off the same ledger as USD and both are EMPTY WHEN THE LEDGER NAMES NONE:
// a task that has written no spend row has no known model and no known
// token count, which is not the same fact as a model called "" or a count
// of zero, and a surface draws nothing for either.
Model string `json:",omitempty"`
Tokens int `json:",omitempty"`
// Started is when the task was created and Ended when it completed; a task
// still open carries the zero Ended. A PROGRAM's task carries its run's one
// pair instead — the hand-off and the instant the program was gone
// (task_run_clock.go's [planRunClocks.apply]) — because the store's pair
// brackets the copy being cut at one end and whenever each kind of ending
// wrote the store at the other.
Started time.Time
Ended time.Time
// Note is the text of the task's last note, empty when nobody has left one.
Note string
// Live is the step the task is running right now — its number, the command
// its worker asked the belt to run, and the moment the command started — read
// off the store's live row. Its zero value is the honest answer for a task
// that is running nothing, which the emptiness law turns into no line drawn
// at all; the worker clears the row the moment the command ends, and every
// ending of its loop, so a task that is not running never claims a present.
Live plandb.LiveStep
LiveParts []PlanCommandPart
// Program is the name of the program this task was handed to — senior-dev —
// read off the program record in the task's own record folder
// ([planProgramRecord]), and empty for every task a worker of this
// conversation's own drives. Stage is the word for where that program says
// it is right now — the step of its own process, `explore`, or before it
// has named one its stage's word — its live step read without its name in
// front ([planProgramStage]), and empty whenever nothing is live. The rail draws
// both under the run's own row, where a program's run used to wear only its
// clock.
Program string
Stage string
// TrajectoryPath is the file the task's steps are recorded in, for a reader
// that wants the record itself and not only its length.
TrajectoryPath string
// Folder is the run copy this row works in. A surface says it once in the
// page head and may omit only a leading change into this exact directory.
Folder string
// Archived is true for a row read from an ENDED run's store, one of the
// runs this conversation finished before the one it holds now. The rows
// arrive oldest run first, so a reader that wants the live run first — the
// digest in front of the person's sentence — has to be able to tell them
// apart without reopening a store ([planDigestOrder]).
Archived bool `json:",omitempty"`
}
PlanTaskRow is one row of the chat's plan. It carries what a surface draws without touching the store again: the task's identity and standing, the seat its shape gives it (RoleOf), its parent, the money its own spend rows carry, the span it covered, the last thing anybody said on it, and where its record lives.
func (PlanTaskRow) StateWord ¶
func (row PlanTaskRow) StateWord() string
StateWord is the row's state IN THE WORDS THE RAIL DRAWS: queued, running, done, stopped, incomplete, your call. It is the ONE mapping from the store's own words (pending, ready, claimed, failed, cancelled, paused) to the ones a person reads, and it lives beside the row so every reader of a row — the side list, the `tasks` listing, the digest in front of the person's sentence — says the same word about the same row. Two readings of one row were two states to the model: the digest said `cancelled` and `claimed` of rows the person saw as `stopped` and `running`.
A status this build has never heard of reads as NOTHING rather than a word invented for it — the emptiness law, applied to a vocabulary that may grow.
type PlanTaskWork ¶
type PlanTaskWork struct {
// Dir is the run's copy, the directory its workers type in.
Dir string
// Patch is the unified difference between the commit the copy was cut
// from and the files in it now, the harness's own files left out
// ([harnessWrote]). It is capped at [planWorkPatchCap] bytes, and Cut says
// the cap was reached.
Patch string
Cut bool
// Added is every file in the copy git has never been told about, relative
// to the copy, the harness's own left out. A new file a worker wrote and
// did not stage is work too, and a patch alone would not show it.
Added []string
// Read says the copy was found and git answered for it. False is a run
// whose copy was never written down, or is no longer on disk, or is not a
// repository: a page has nothing to show and says so.
Read bool
// NoDoor says the engine that was asked has no working-copy read at all.
// It is set by a client whose engine answered "no such method", and it
// does not travel on the wire: a current engine never sets it, and a page
// draws the absence sentence rather than an empty difference.
NoDoor bool `json:"-"`
}
PlanTaskWork is what a run's working copy holds that its ground did not: the patch, the files git has never seen, and where the copy is.
ITS ZERO VALUE IS "NOTHING KNOWN", and a surface draws it as the absence it is. An empty Patch on a copy that was read is a run that has changed nothing yet, which PlanTaskWork.Read tells apart from a copy nobody could read.
type PlanWorkAgent ¶
type PlanWorkAgent interface {
PlanTaskWork(id string) (PlanTaskWork, bool)
}
PlanWorkAgent is the optional door onto a run's working copy. It is its own interface rather than a method on PlanAgent because only an engine that holds the copy on its own disk can answer it: a surface asserts it, and a page on an engine without it draws the tab's honest absence.
type Policy ¶
type Policy struct {
Kind PolicyKind `json:"kind,omitempty"`
// After is how long a PolicyRecommendThenAuto waits before it takes the
// pick. It is zero on every other kind.
After time.Duration `json:"after,omitempty"`
}
Policy is what may answer this question by itself, and after how long.
ITS ZERO VALUE WAITS, which is the whole safety of this type: a lane that forgets to fill it in gets the behaviour every lane has today.
type PolicyKind ¶
type PolicyKind string
PolicyKind is what may answer a question without a person present.
const ( // PolicyAsk waits. It is the zero value, and it is the floor every other // value has to be raised above deliberately. PolicyAsk PolicyKind = "ask" // PolicyRecommendThenAuto shows the pick, waits [Policy.After], and then // takes the pick itself, recording DecidedByDial. It is meaningless // without a pick and refused on irreversible stakes. PolicyRecommendThenAuto PolicyKind = "recommend-then-auto" // PolicyDecide takes the pick at once and says so. It is how a person turns // a whole kind of question off, and it is never a default. PolicyDecide PolicyKind = "decide" )
type PresenceJob ¶
type PresenceJob struct {
// ID is the job's own number, decimal — the handle `jobs output 3` and
// `jobs kill 3` take inside the session that holds it. It restarts at one
// in every window, so it is joinable only together with the session's id.
ID string `json:"id"`
// Title is the row's short name ([jobRowTitle]): a watch or a render's
// label, or a plain command's own first line.
Title string `json:"title"`
// Dir is the folder the process was started in. A surface names the place
// from it; it is empty only on a job a test registered by hand.
Dir string `json:"dir,omitempty"`
// StartedAt is when the process forked, so a surface counts the job's age
// up on its own beat.
StartedAt time.Time `json:"startedAt,omitzero"`
}
PresenceJob is one background job a live session has running right now — a dev server, a watch, a long build — as another window reads it. It is the same list the conversation's own column draws ([Agent.jobsWorkingNow]), and it is here so home can draw it without holding the agent that forked it.
IT IS A CLAIM ABOUT A PROCESS AND NOTHING ELSE. A job's log, its exit code and its output stay where the job's own window keeps them; a finished job is simply absent from the next refresh, because a job's ending is news its own conversation tells (jobrow.go's header) and this file keeps no record.
type PresenceQuestion ¶
type PresenceQuestion struct {
// Kind is which lane raised it (answers.go).
Kind QuestionKind `json:"kind"`
// ID is the token the answer names — [Event.ID] for a consent request, the
// node's id for a proposal, [StandingNotice.ID] for a card. It is the same
// number the surface in that window hands its own resolver.
ID uint64 `json:"id"`
// Text is the one line the session is stopped on, and it is the SAME line
// [SessionPresence.Reason] carries — one sentence, written once, read in two
// places for two purposes.
Text string `json:"text,omitempty"`
// Options are the answers, in the order chips are drawn for them.
Options []AnswerOption `json:"options,omitempty"`
// Asked is when the question was put. A surface may draw its age; nothing
// judges freshness by it, because the FILE's stamp is what says whether any
// of this is still true (see [SessionPresence.Fresh]).
Asked time.Time `json:"asked,omitzero"`
// Full is the WHOLE question (question.go), where the lane that raised it
// could describe one — the evidence, the asker's own pick, what is waiting
// on it, what an answer costs and how long it may last.
//
// THE FOUR FIELDS ABOVE STAY FILLED BESIDE IT, and that is the whole reason
// this is a pointer on the end rather than a replacement: presence files are
// read by BUILDS OF OTHER AGES, on this machine and across a shared disk,
// and a window that only ever knew Kind, ID, Text and Options must go on
// answering exactly as it did. A build that knows this field draws the
// object; a build that does not draws the line and the chips, which is what
// it always drew.
//
// IT IS STILL THE SHORTEST THING SOMEBODY COULD ANSWER FROM in the sense
// this struct's header means it: the whole question is the asker's own
// account of the decision, not a second rendering of the row it is about —
// [SubjectRef] points at that row and never copies it.
Full *Question `json:"full,omitempty"`
}
PresenceQuestion is the card this session is stopped on, as another window sees it: what it is asking, and what it will take for an answer.
IT IS THE SHORTEST THING SOMEBODY COULD ANSWER FROM, and that bound is the design. The card itself — the command's arguments, the brief, the whole standing item — stays in the window that raised it; what travels is the one line a person reads and the two or three answers they would give. A presence file is read by every window every few seconds, and a card copied into it would be a second rendering of a question, which is how a person comes to approve something other than what they read (consent.go's own law about the row the block draws against).
THE OPTIONS ARE WRITTEN DOWN RATHER THAN DERIVED BY THE READER, even though AnswerOptions would answer the same thing on this build. They are THE WRITER'S account of what it will accept: a surface draws the chips the session that is waiting offered, so it can never advertise a key that session would drop.
func (PresenceQuestion) Answerable ¶
func (q PresenceQuestion) Answerable() bool
Answerable reports whether this question is one another window could answer: it came from a lane, it names an id, and it offered at least one key.
func (PresenceQuestion) Label ¶
func (q PresenceQuestion) Label(key string) string
Label is the word THIS question offered for one key, and "" for a key it did not offer.
IT IS THE WRITER'S LIST AND NOT THE KIND'S. AnswerLabel answers what a kind of question can take in general; this answers what the session on the other end of this file said it would take, which is narrower whenever the answers depend on what is being asked (StandingOptions). A surface deciding whether a keypress is an answer must ask THIS one, or a digit the chips never drew would still be sent.
type PresenceState ¶
type PresenceState string
PresenceState is what one session is doing, in the words a person would use.
const ( // PresenceWorking says a turn is running: the model is thinking, a tool is // out, work is happening. PresenceWorking PresenceState = "working" // PresenceWaiting says the session has asked its person something and can // go no further until they answer. It is the most valuable thing this file // says, and it OUTRANKS working: a turn blocked on a question is running in // the sense that a process exists, and stopped in every sense a person // cares about. PresenceWaiting PresenceState = "waiting on you" // PresenceIdle says nothing is running and nothing is being asked. The // session is open and the cursor is blinking. PresenceIdle PresenceState = "idle" )
type PresenceTask ¶
type PresenceTask struct {
// ID is the node's id inside the session that is running it, decimal —
// [TaskIndexEntry.ID]'s own spelling, so a row here and a row there about
// the same node are joinable on (SessionID, ID).
ID string `json:"id"`
// Title is the task's title, uncut.
Title string `json:"title"`
// State is the node's own word — "running" or "queued". Only unsettled
// nodes are written here at all, so it is never a landed state: work that
// finished is the index's to report, not presence's.
State string `json:"state"`
// StartedAt is when the node began, and it is zero for a queued node that
// has not. A surface drawing an age must read that emptiness as "not yet"
// rather than as an age of zero (the emptiness law).
StartedAt time.Time `json:"startedAt,omitzero"`
// Phase is WHICH OF ITS LIVES the node is in, in the words task_contract.go
// exports ([TaskPhaseChecking] and the others) — the same spelling the pulse
// on disk and [EventTaskPhase] carry, so a row drawn in another window and
// the card in front of the person are never two vocabularies for one moment.
//
// IT IS HERE BECAUSE `running` STOPS BEING THE WHOLE TRUTH FOR MINUTES AT A
// TIME. The state stays `running` across a worker, a check and every repair
// round, so a window reading only [PresenceTask.State] drew `running` while
// the node had been under a check for four minutes — the same silence the
// live window's own row was fixed for.
//
// AND WORKING IS WRITTEN AS NOTHING. A node getting on with the work is what
// a running row has always meant, so the ordinary life is left off the file
// entirely rather than spelled out: the phase is written only where it is
// news, and an absent field draws exactly the row it drew before this
// existed — which is also every row written by a build older than this field.
Phase string `json:"phase,omitempty"`
// Files are the paths this node has written SO FAR — repo-relative,
// slash-spelled, in the order it first wrote them, capped at
// [taskFilesLimit] ([TaskNode.wrote] is where they accumulate).
//
// IT IS A FACT AND NOT AN INTENT, and that is the whole of why it belongs in
// this file rather than in a plan somewhere. A path is here because a saving
// call came back successful; nothing about what the node MEANS to write is
// knowable, and a claim staked on an intention would be a window reserving
// files it never touched. It refreshes with the ordinary heartbeat, like
// everything else here, and it goes stale with the rest of the row.
//
// EMPTY IS UNKNOWN AND NEVER "TOUCHES NOTHING". A node that has not written
// anything yet, and a session running a build too old to say, look exactly
// alike here. A reader that took either for "this work is nowhere near my
// files" would be inventing the one answer this field cannot give — so
// [Elsewhere.Touching] answers with two lists and keeps them apart.
Files []string `json:"files,omitempty"`
// Activity is the one line saying what the node's worker is doing — the
// call in flight and how long it has been out, or the gap between calls with
// the step count beside it. It is [TaskIndexEntry.Activity]'s line, from the
// same recorder (task_live.go), carried across the window that line never
// leaves.
//
// IT IS WRITTEN ONLY WHILE THE WORKER IS THE LIFE THE NODE IS IN. Through a
// check, a repair round or the sizing read, the recorder still holds the
// worker's last call, finished — a line asserting a present that has passed
// — so the field is left off and [PresenceTask.Phase] says what is true. It
// is empty too for a queued node, which has no worker yet, and for every row
// written by a build older than this field; a surface draws nothing for any
// of them.
Activity string `json:"activity,omitempty"`
// Done and Total are how far work that COUNTS ITS OWN PROGRESS has got: for
// an ADAPTIVE RUN, nodes settled of the nodes its planner has laid out so far
// ([orchestrate.Snapshot]); for a QUICK TASK, items ticked of the items on its
// list so far — the same two numbers its own row reads as `quick · 2/4`. Total
// moves in both, as the planner amends the graph or the worker adds a step, so
// this is a count and never a promise of the end.
//
// BOTH ARE ZERO FOR WORK THAT DOES NOT COUNT THIS WAY — an ordinary task, a
// quick task with no list, and a run whose planner has not laid anything out
// yet — and a surface draws no `0 of 0` for them (the emptiness law).
Done int `json:"done,omitempty"`
Total int `json:"total,omitempty"`
// Kind is what sort of node this is ([TaskKind]), and absent for the
// ordinary one — an adaptive run's row, which is not a node, leaves it off
// too. It crosses the window because the two kinds are different facts to a
// reader deciding whether to start the same work: a task writes in a copy of
// its own and comes home through a merge, and a quick task writes IN THAT
// WINDOW'S FOLDER while it runs.
Kind TaskKind `json:"kind,omitempty"`
// Parent is the id of the node that handed this one out, in [PresenceTask.ID]'s
// own spelling, and absent for work the conversation started itself. It is
// what lets a reader fold a family onto one row: eight quick parts under one
// task are one piece of work with eight hands, and read flat they were eight
// unrelated jobs — exactly the picture that makes another window's model
// propose the same work a ninth time.
Parent string `json:"parent,omitempty"`
}
PresenceTask is one piece of work a live session has out right now: a task node, or an adaptive run.
It carries what the work is doing AT THIS INSTANT — which of its lives it is in, the call in flight, how far a run has got — and never a cost, a token count or an outcome, and that is the whole distinction from TaskIndexEntry: this is the shortest thing that lets another window draw a row saying what is happening. Everything about what the work CAME TO is in the project index, which is the file that answers questions about work rather than about processes.
type Principal ¶
type Principal interface {
// Ask is the goal in the principal's own words, verbatim and never
// interpreted. Empty before anything has been asked.
Ask() string
// Acceptance is what the WHOLE ask has to satisfy before the work is
// finished, as one observable sentence. Empty means this principal holds
// no acceptance of its own — which is a person, who is looking at the
// work and does not need one written down.
Acceptance() string
// Budget is what may still be spent. The zero Budget is no ceiling at all,
// which is what an attended session has always had.
Budget() Budget
// Report is told how one unit of work landed, and answers WHAT TO OPEN
// NEXT — a brief, in the words the next attempt should start on. An empty
// answer means nothing more is started on the strength of this landing,
// which is every landing for a person: they read the news and decide.
Report(Landing) string
// Decide answers the end of a turn that stopped: carry on with a brief, or
// the ask is finished, or stop and say why.
Decide(Remains) Decision
}
Principal is the addressee of every "ask the person" path in this package.
FIVE METHODS, AND EACH ONE IS A QUESTION THE ENGINE USED TO ANSWER FOR ITSELF. Ask and Acceptance are what the work is measured against; Budget is what may be spent measuring it; Report is one unit of work coming home, and what — if anything — to open next on the strength of it; Decide is the end of a turn that stopped.
IT IS DELIBERATELY NOT A STRUCT OF CALLBACKS. Two implementations exist and the whole point of the interface is that a third — a person on another machine, a queue, a scheduled owner — can be written without any road in this package learning a new name.
type ProgramEnding ¶
type ProgramEnding struct {
// Status is delegate.StatusFail, StatusBudget, StatusCrashed, or a word
// this build does not know.
Status string
// Reason is the sentence: `senior-dev did not finish: …`.
Reason string
// Result is the program's account in full.
Result string
}
ProgramEnding is a delegated run's program's own ending when it did not finish, as the engine read it off the program's terminal record: the status word, the one sentence the row says, and the program's account.
type ProgramFolder ¶
type ProgramFolder struct {
// Program is the program's name, and Title is the run's.
Program string `json:"program"`
Title string `json:"title"`
// Dir is the folder the program works in: its copy for a run in a
// repository, and otherwise the folder itself, spelled the way the door
// asked for it.
Dir string `json:"dir"`
// Repo is the person's repository a run's copy was cut from, empty for a
// folder worked in itself ([ProgramFolder.Copied]). Linked is the ignored
// names linked into the copy from it ([programCopyLinks]), and LeftBehind
// the paths its checkout had not committed when the copy was cut, which
// the copy does not have ([ProgramFolder.LeftBehindWords]).
Repo string `json:"repo,omitempty"`
Linked []string `json:"linked,omitempty"`
// Carried is the ignored names put into the copy as its own — a file
// copied, a folder cloned — rather than linked ([carryOne]).
Carried []string `json:"carried,omitempty"`
LeftBehind []string `json:"leftBehind,omitempty"`
// Untracked names the person's untracked files copied into the copy as
// its inputs, and Inputs is each one's fingerprint as it was copied
// (gitidentity.Inputs): an input the run leaves as it was stays out of
// every commit, and one it changes is its work and goes on its branch.
Untracked []string `json:"untracked,omitempty"`
Inputs gitidentity.Inputs `json:"inputs,omitempty"`
// Snapshot is the commit that carries those uncommitted changes into the
// copy, the first on the program's branch, whose parent is Start; empty
// when there were none, or when they could not be carried, which
// LeftBehindWhy then says ([ProgramFolder.snapshotLeftBehind]). A run that
// carries on an earlier run's branch carries its snapshot too, so the work
// it counts is the programs' and never the person's.
Snapshot string `json:"snapshot,omitempty"`
LeftBehindWhy string `json:"leftBehindWhy,omitempty"`
// Branch is the program's own branch, cut by codeaf; empty for a folder the
// program works in without git. Home is the branch the person had checked
// out, empty when their checkout was on no branch, and Start is the commit
// it stood on, which the branch was cut from.
Branch string `json:"branch,omitempty"`
Home string `json:"home,omitempty"`
Start string `json:"start,omitempty"`
// Outer is a repository around a folder worked in without git, which codeaf
// cut no branch in because its root holds the home folder.
Outer string `json:"outer,omitempty"`
// Notes is the program's notes folder inside Dir, and NotesWereThere says
// it was already there when the run began, which leaves it where it is.
Notes string `json:"notes,omitempty"`
NotesWereThere bool `json:"notesWereThere,omitempty"`
// Keep is the run's record folder ([ProgramFolderOrder.Keep]) and
// SignModel the model its attribution line names
// ([ProgramFolderOrder.SignModel]).
Keep string `json:"keep,omitempty"`
SignModel string `json:"signModel,omitempty"`
// NoAttribution is true when the run had no answered model call, so its
// finishing commit does not credit a model that did no work in this run.
NoAttribution bool `json:"noAttribution,omitempty"`
// Passed says the program's own checks passed at the end of this run.
// The caller sets it from the outcome; the default carries the ending
// after the program's message. It is never persisted, because a gone run
// has no completed outcome to trust.
Passed bool `json:"-"`
// Ended is the sentence the run's folder was finished with. Empty is a
// folder still owed its ending.
Ended string `json:"ended,omitempty"`
// Continues says this run carries on on the branch an earlier run of the
// same line left its work on, rather than cutting one of its own
// ([ProgramFolderOrder.Carry]): Branch, Home and Start are that line's, so
// what the earlier runs did is never counted as this one's nothing.
Continues bool `json:"continues,omitempty"`
// From is the branch this run's own was cut from when that is an earlier
// run's rather than the person's checkout: a run of the same line after one
// whose work passed starts on top of that work, on a new branch, so the
// passed branch holds that run's work and nothing after it
// ([programCarry.Fresh]). Start is then that branch's tip, so the files the
// ending counts are this run's own.
From string `json:"from,omitempty"`
// ResumedAt is the commit the branch a run carries on stood at when this
// run began ([ProgramFolder.Continues]): the earlier runs' work, and
// whatever the branch was given between the runs — a rebase onto newer
// history for its pull request among them. The files the ending counts are
// measured from it, so they are this run's own ([ProgramFolder.ownBase]).
// Empty for every other run, and in a record an older build wrote.
ResumedAt string `json:"resumedAt,omitempty"`
// IgnoredAtStart keeps paths git ignored before the run changed its rules,
// together with the person's untracked inputs copied into the worktree.
IgnoredAtStart []string `json:"ignoredAtStart,omitempty"`
// IgnoredOuter is the enclosing repository when Dir itself is ignored by it.
IgnoredOuter string `json:"ignoredOuter,omitempty"`
// contains filtered or unexported fields
}
ProgramFolder is one program run's folder as PrepareProgramFolder readied it. It is also the record a later process settles the run's folder from when the process that started it went away first ([settleOwedProgramFolder]), which is why its fields are written down.
func PrepareProgramFolder ¶
func PrepareProgramFolder(order ProgramFolderOrder) (*ProgramFolder, error)
PrepareProgramFolder readies the folder a program was asked to work in, per the contract at the top of this file, and holds it for the run: the folder resolved and made when it must be, the hold taken, a run that went away in it settled first, and in a repository the checkout read and the program's branch cut. The refusal is a sentence a person can act on, and nothing of the person's has been changed when there is one.
func (*ProgramFolder) BriefNote ¶
func (f *ProgramFolder) BriefNote() string
BriefNote is the line a program's brief opens with when it works in a copy: where the copy is, and that a path the brief names under the person's repository is the same file in the copy. "" for every other run.
A BRIEF IS WRITTEN ABOUT THE PERSON'S FOLDER, because that is the one the conversation can see. A program that took its paths literally would read the person's files and have its writes refused, or — through its shell — make them in the person's checkout, outside the copy its work is committed from.
func (*ProgramFolder) Copied ¶
func (f *ProgramFolder) Copied() bool
Copied says the program works in a copy of its own of a repository, rather than in a folder itself.
func (*ProgramFolder) Finish ¶
func (f *ProgramFolder) Finish(result string) ProgramFolderEnd
Finish ends a program's run in its folder, per the fourth point of the contract at the top of this file, and lets the folder go. result is the run's ending in words, used below the title when there is no usable message, and after its message when the run did not pass.
func (*ProgramFolder) Ground ¶
func (f *ProgramFolder) Ground() string
Ground is the folder the run's work is about, as a person names it: the person's repository for a run in a copy of it, and the folder itself for every other.
func (*ProgramFolder) Hold ¶
func (f *ProgramFolder) Hold() *os.File
Hold is the open file the run's hold on its folder is taken on, for the door that starts the program to hand to the program's process (delegate.Launch.Hold); nil when there is none.
THE HOLD LIVES AS LONG AS THE LAST PROCESS THAT HAS IT. It is a flock, which belongs to the open file and not to the process that opened it, so with the program holding the same file a codeaf that dies leaves the folder held until the program has stopped too: the next codeaf never finishes a copy — commits it, removes it — while the program is still writing its last edits there.
func (*ProgramFolder) IgnoredFile ¶
func (f *ProgramFolder) IgnoredFile() string
IgnoredFile is the run's start-time ignore list, kept outside the repository so the child's recorder still keeps those paths out of its trees after the run changes .gitignore.
func (*ProgramFolder) InputsFile ¶
func (f *ProgramFolder) InputsFile() string
InputsFile is the run's list of its copy's inputs and their fingerprints (ProgramFolder.Inputs), kept beside ProgramFolder.IgnoredFile for the child's recorder to read (gitidentity.InputsEnv); "" when there is none.
func (*ProgramFolder) LeftBehindWords ¶
func (f *ProgramFolder) LeftBehindWords() string
LeftBehindWords is what a run in a copy says, as it starts, about the changes the person's checkout had not committed: carried into its copy, or not and why; "" when there were none, and for every run not in a copy.
func (*ProgramFolder) Plain ¶
func (f *ProgramFolder) Plain() bool
Plain says the program works in its folder without git.
func (*ProgramFolder) StopPromise ¶
func (f *ProgramFolder) StopPromise() string
StopPromise is what a person who stops a program's run is told at once about where its work will be.
type ProgramFolderEnd ¶
type ProgramFolderEnd struct {
Folder ProgramFolder
// Changed is every path the program's branch changed from where this run
// started ([ProgramFolder.ownBase]).
Changed []string
// Kept says the program's branch holds its work: for a run that carries on
// an earlier run's branch, the line's work, so a run that adds nothing
// still lands on the branch that holds it. Added says this run changed the
// branch's tree from where it found it, which for every other run is Kept.
Kept bool
Added bool
// Upstream is the remote branch the program's branch tracks, as
// `<remote>/<branch>` — set only for a live remote branch of its own name
// on a plainly named remote — and UpstreamRemote and UpstreamRef its two
// halves. Such a branch is brought in by pushing it,
// not by merging it into the person's checkout ([ProgramFolderEnd.mergeWords]).
Upstream string
UpstreamRemote string
UpstreamRef string
// SnapshotHeld says a branch a run carries on still holds the change the
// line's first run carried the person's uncommitted changes in with
// ([ProgramFolder.Snapshot]) — that commit, or the one a rebase since wrote
// in its place ([branchHoldsChange]). A run that cut its own branch begins
// with that commit, and is not asked.
SnapshotHeld bool
// Dropped says the program's branch was deleted because it holds
// nothing: for a run in a copy, one readied and never started
// ([ProgramFolder.abandon]); for a run in the person's checkout itself,
// written by a build before programs worked in copies and still read back
// from its record, one that changed nothing and was switched back.
Dropped bool
// Moved says HEAD was not on the program's branch when the run ended:
// HeadOn is the branch it was on, empty with At naming the commit when it
// was on none.
Moved bool
HeadOn string
At string
// HomeMoved says the person's own branch no longer points where it did
// when the run began — something committed on it, reset it or deleted it
// while the program worked — and HomeAt is the commit it points at now,
// empty when it is gone. codeaf moves it back no more than it moved it.
HomeMoved bool
HomeAt string
// Gone says the run's process went away before it could end the run
// itself, so a later codeaf settled its folder ([ProgramFolder.settleGone]):
// a copy finished as any ending finishes it, a folder the person works in
// read and never written. Uncommitted is how many files are not committed
// in a checkout left on or moved off the task branch.
Gone bool
Uncommitted int
// Committed says codeaf made the commit that finishes the run: there was
// something left to commit, and it went. A run whose copy held nothing
// more — or whose copy was gone — has only the commits it made itself.
Committed bool
// Patch is where what a program left in its copy was kept when it could
// not be committed on its branch ([programLeftoversFile]), and CopyLeft
// the copy itself when it is still on disk; both empty otherwise. CopyKept
// is why a copy was left on purpose — what is in it could be neither
// committed nor kept anywhere else — and empty for one that would not go.
Patch string
CopyLeft string
CopyKept string
// Frozen is the copy's own refs that held something its branch does not,
// each put on a branch of its own before the copy went
// ([ProgramFolder.keepOwnRefs]).
Frozen []programKeptRef
// Saved is the branch codeaf put on commits the program made on a
// detached HEAD in its copy, which nothing else would have kept
// ([ProgramFolder.keepDetached]).
Saved string
// Refused is git's own line when what the program left could not be
// committed, or the checkout could not be put back.
Refused string
// Notes is where the program's notes went, as a sentence.
Notes string
// contains filtered or unexported fields
}
ProgramFolderEnd is how a program's run left its folder, as ProgramFolder.Finish found it and made it.
func (ProgramFolderEnd) Sentence ¶
func (e ProgramFolderEnd) Sentence() string
Sentence is how a run left its folder, in the one sentence the run's page, the conversation and a shell run's last lines all say: where the work is, how much of it, that its branch is checked out, and how to go back to the person's own branch and bring the work in.
type ProgramFolderOrder ¶
type ProgramFolderOrder struct {
// Program is the program that will work in the folder.
Program delegate.Delegate
// Dir is the folder asked for, absolute.
Dir string
// Title is the run's title: the program's branch is named from it, and
// the commit that finishes the run carries it. Empty is the first words of
// Brief, the way a task names itself from its brief ([taskPersonTitle]).
Title string
Brief string
// Holder is how a second run on the folder is told whose run holds it:
// `task 4 (Fix the parser)`, or `a run started at a shell`.
Holder string
// Keep is the run's record folder. The program's notes are moved into it
// when the run ends, how the folder was left is written there
// ([programFolderEndFile]), and it is the name a reopen finds the run's
// folder by ([settleOwedProgramFolder]).
Keep string
// Instead is what a folder that is the home folder is answered with,
// after the refusal itself ([programHomeRefusal]).
Instead string
// Place is the conversation's session folder, which says where the
// repository's git lock lives ([lockGitRoot]); zero for a shell run,
// which takes none.
Place Place
// Carry is the branch an earlier run of the same line left its work on,
// which this run carries on in its copy ([programCarryOf]); nil for a
// line's first run and for every shell run.
Carry *programCarry
// SignModel is the model the attribution line on the commit that finishes
// the run names, and "" for the line that names none: codeaf signs every
// commit it writes, and the only choice is whether the model is named
// ([gitSignature]).
SignModel string
}
ProgramFolderOrder is what a door hands PrepareProgramFolder.
type Project ¶
type Project struct {
// Bucket is the encoded directory name under the places root. It is an
// address and never a name — see this file's header.
Bucket string
// Dir is the bucket's full path.
Dir string
// Path is the real workspace the sessions recorded, and "" when not one of
// them said. It is read out of meta.json, which is the only authority.
Path string
// Name is what to CALL this project on a row: the last element of Path, `~`
// for the home directory itself, and the encoded bucket name when nothing
// recorded a path. The emptiness law is kept by never inventing a third
// answer — a project with no name shows the name it does have.
Name string
// Sessions are the conversations held here, in triage order (see
// [sortSessions]).
Sessions []SessionRow
}
Project is one bucket: a workspace, the conversations held in it, and the index of work they commissioned.
func (Project) At ¶
At is when somebody last spoke in this project, which is its first session's stamp only when nothing is live — so it is taken over the whole list.
func (Project) NeedsPerson ¶
NeedsPerson is how many of this project's conversations are stopped waiting on somebody. It is the one count worth putting on a heading: everything else a project can say is about work that is moving or work that is over, and this is the number that means "come back here".
type ProposedAddition ¶
ProposedAddition is conversations the model would add to a team that exists, by the team's id and the members' keys.
type ProposedTeam ¶
ProposedTeam is a new team the model suggests: its cleaned name, the keys of its members, and a few words on why.
type Question ¶
type Question struct {
// ClarificationDepth orders prerequisites ahead of the question they explain.
ClarificationDepth int `json:"clarificationDepth,omitempty"`
// ID is the token an answer names, and it is THE SAME NUMBER the lane's own
// resolver already takes — [Event.ID] for a consent request, the node's id
// for a proposal, [StandingNotice.ID] for a standing card. A question does
// not mint an id of its own, because a second id for one decision is a
// second thing an answer could name and get wrong.
ID uint64 `json:"id"`
// Ref is that token where the lane's is a STRING rather than a number — a
// connect account, an adaptive run. Exactly one of ID and Ref is set.
Ref string `json:"ref,omitempty"`
// Kind is the LANE: which part of the engine is stopped, and therefore
// which resolver [Agent.ResolveQuestion] applies the answer through.
Kind QuestionKind `json:"kind"`
// Ask is the SHAPE of the decision (see [AskKind] and the file header on
// why these are two fields and not one).
Ask AskKind `json:"ask"`
// Form is the smallest drawing the evidence allows. A surface may promote
// it and may never demote it.
Form QuestionForm `json:"form,omitempty"`
// Asker is who is asking, for the dim attribution beside the head.
Asker Asker `json:"asker,omitzero"`
// Head is the question in one sentence, in the asker's own words and in a
// person's vocabulary — never "approval", "gate", "prompt" or "modal".
Head string `json:"head"`
// Reason is WHY NOW, in one dim sentence: the policy's own phrasing, the
// task's reason, what changed. It is the half a person acts on, and
// [Question.Check] refuses a question without one.
Reason string `json:"reason,omitempty"`
// Subject is the row a surface has already drawn for this, which is the row
// the question attaches to. The question never carries a second copy of it.
Subject SubjectRef `json:"subject,omitzero"`
// Options are the answers the asker wrote down, in the order chips are
// drawn. THEY ARE THE WRITER'S ACCOUNT OF WHAT IT WILL ACCEPT — a surface
// draws these and never a list of its own, so it can never offer a key the
// engine would drop (taskpresence.go's law about the presence file's
// options, which is the same law one layer up).
Options []AnswerOption `json:"options,omitempty"`
// Input is what a person may give besides a pick.
Input InputShape `json:"input,omitzero"`
// Pick is the asker's own answer, or nil where it genuinely has none.
Pick *Pick `json:"pick,omitempty"`
// Stakes is what a wrong answer costs, and it is what decides whether a
// clock is allowed at all.
Stakes Stakes `json:"stakes"`
// Policy is what may answer this without a person.
Policy Policy `json:"policy,omitzero"`
// Blocking is what is paused on it. Its zero value means NOTHING is, which
// is the honest reading for a ratification and for most landings.
Blocking Blocking `json:"blocking,omitzero"`
// Batch is WHICH STEP OF A TURN RAISED IT, and it is the one thing on this
// object that is about the question's NEIGHBOURS rather than about itself: a
// model that calls three tools at once can put three questions on somebody's
// screen in the same instant, and those are one thing to answer rather than
// three ([Agent.stepToken] mints it, and the lanes raised from inside a tool
// batch — the approval gate and the model's own `ask` — are the ones that
// carry it). Empty is a question raised outside any step, which is every
// landing and every fuel gate: those have no neighbours to group with.
Batch string `json:"batch,omitempty"`
// Later says THE ASKER IS NOT WAITING FOR THIS and will read the answer
// whenever it comes, so the turn that raised it may end without it. It is
// the model's own word about its own question — the `ask` tool sets it when
// the call says nothing is blocked on the turn — and it is what keeps such a
// question off the sweep that retires everything a turn leaves behind
// ([questionOutlivesTurn]).
//
// A RATIFY DOES NOT CARRY IT, and that is not an oversight: a ratify says
// something reversible was already done and is answered in the person's own
// time, but it belongs to the turn that did the thing, and one still
// standing when that turn ends is a question about work nobody is doing.
Later bool `json:"later,omitempty"`
// Scope are the lifetimes an answer may carry, in the order they are
// offered. Empty means the answer is [ScopeOnce] and nothing wider was ever
// on the table.
Scope []AnswerScope `json:"scope,omitempty"`
// Attach is the evidence at the head — what a person reads before the
// answers. It is what sets [Question.Form].
Attach []Block `json:"attach,omitempty"`
// Asked is when it was put.
Asked time.Time `json:"asked,omitzero"`
// Deadline is when a clock takes the question, and it is ZERO ON EVERY
// QUESTION THAT HAS NO CLOCK — which is all of them but the task proposal
// today. A WAIT THAT ENDED IS NOT A NO (consent.go): what a deadline does is
// written by the lane, and for the proposal lane it APPROVES.
Deadline time.Time `json:"deadline,omitzero"`
// Withdrawn is set when the question stopped being one, and nil while it
// stands.
Withdrawn *Withdrawal `json:"withdrawn,omitempty"`
}
Question is a decision handed to a person with its evidence attached.
It is ONE object for every lane in this engine (see the file header), and every field on it is either the asker's own account of the decision or the engine's account of what is waiting on it. Nothing here is a rendering: the forms are internal/tui3's, and two of them drawing the same value differently is a surface question rather than a contract one.
func ConnectQuestion ¶
ConnectQuestion is that object, and it is ONE BUILDER for the two roads it arrives by: this lane's own Agent.OpenQuestions, and the surface, which has the event a moment before the questions lane reaches it and builds the same question from it (tui3's connect.go). The block keys a question by its lane and its token, so two builders would be two questions replacing each other on screen while somebody read one of them.
The surface knows three things this engine does not — the word a person owns the account by, the service's own sentence over the box, and whether what it wants is a secret or the part of an address — so they are arguments with honest defaults rather than facts invented here.
AN ACCOUNT THAT NEEDS A TYPED ANSWER IS A QUESTION WITH A BOX RATHER THAN A PICK, because a bare yes to one of those is read as a decline (connect.go) and an answer that means no while reading yes is worse than no answer.
func HarnessQuestion ¶
HarnessQuestion is that object, built from the card the lane already holds: Text is the harness's name and Hint its one sentence.
IT IS ONE BUILDER AND NOT TWO. Every other lane in this file has a twin on the surface — a window has the event before the questions lane reaches it and raises the question from that, so the two must be one sentence (TaskProposalLead states the cost of a drift). This lane is the first to be written the honest way round: the surface calls THIS, so there is nothing to drift.
func (Question) Check ¶
func (q Question) Check(records []DecisionRecord) error
Check is THE QUESTION GATE: the last rung of the ladder, defended.
It refuses rather than repairs (see the file header), and it consults the decision record: a question whose head matches an answer already given about the same subject is refused with what was decided, so the asker acts on that answer instead of asking a person to give it twice. Passing nil records is legal and means the record was not consulted — every caller inside this package passes Agent.Decisions.
It checks the SHAPE of one question and never how many are open; that bound is QuestionCap and it belongs to whoever is holding the set (see [Agent.checkQuestion]).
func (Question) Option ¶
func (q Question) Option(key string) (AnswerOption, bool)
Option is the answer this question offered under one key, and false for a key it did not offer. A surface deciding whether a keypress is an answer asks THIS rather than AnswerOptions, for the reason PresenceQuestion.Label gives: the writer's list is narrower than the kind's whenever the answers depend on what is being asked.
func (Question) Revisable ¶
Revisable reports whether an answer to this question can still be changed after it was given — the one reading, used by the surface that offers the key and by the door that takes the revision, so a receipt can never offer what the engine will refuse.
IT IS KEYED ON WHAT THE ANSWER DID, not on which lane asked. Two properties decide it. An irreversible decision is never revisable: what it allowed has already happened, and "changing" it would be a second decision wearing the first one's receipt. Everything else is revisable exactly where the answer can be GIVEN AGAIN — the model's own question, whose answer is a message and can therefore be sent a second time saying what changed, and a permission, whose widening yes is a grant and can be taken back ([Agent.undoGrant]). Every other lane's answer moved work: a task started, a landing landed, a service connected.
func (Question) SecretAnswer ¶
SecretAnswer reports whether what a person types into this question is a CREDENTIAL rather than a sentence — an API key, a token, the half of an address that authorises it.
IT IS THE ONE READING OF InputShape.Secret, and far more hangs on it than how a box is drawn: [withoutSecretWords] takes a secret's words off every copy of the answer that leaves the lane that asked for them, so a key never reaches the record, the questions lane, a `--host` frame or the model's prompt. A surface asks the same question to mask its box.
func (Question) Token ¶
Token is the question's id as one string, whichever of the two the lane uses. It is what a record is keyed by and what a surface names in a log line; it is never drawn for a person.
func (Question) Waiting ¶
Waiting reports whether ANYTHING IS STOPPED on this question — the one reading of "is somebody being waited for", and the only one the waiting desk, the presence file and the open-question cap are allowed to take.
IT IS TWO TERMS AND BOTH ARE NEEDED. AskKind.Waits is the SHAPE: a ratify waits on nobody by definition, whatever else it carries, and a ratify that carried a blocking of its own would otherwise be back on the waiting desk — which is the defect this exists for. Blocking.Blocks is the FACT: what the lane that raised it says is actually paused, the turn or a task.
The defect, measured on 2026-09-11: a standing ratify and a bash approval standing at once, and home said `waiting on you` with the RATIFY's line on it — because the desk answered with its oldest row and nothing asked whether that row was waiting for anything. Pressing the key answered the ratify.
type QuestionDiscussion ¶ added in v0.4.0
QuestionDiscussion carries a clarification beside the conversation's blocked turn. Its events never finish or replace that turn.
type QuestionForm ¶
type QuestionForm string
QuestionForm is how big the drawing is: THE EVIDENCE SETS THE SIZE. A surface may promote a form — draw a card where a line was asked for, because there is room — and may never demote below what the evidence needs, because folding a diff into one row is showing somebody less than they are deciding on.
const ( // FormLine is one row and one answers row, pinned above the message box. FormLine QuestionForm = "line" // FormCard is a head, a reason, one row per answer, and an answers row. FormCard QuestionForm = "card" // FormRoom is a page over the conversation: answers as sections with their // bodies and blocks, comparison, comments, and a foot that composes the // answer. FormRoom QuestionForm = "room" // FormSheet is many questions from one step or many hands, grouped and // answered together. FormSheet QuestionForm = "sheet" )
type QuestionKind ¶
type QuestionKind string
QuestionKind is which of the three lanes a question came from. The words are the ones the code already uses for them.
const ( // QuestionConsent is the approval gate: may this call run (consent.go). QuestionConsent QuestionKind = "consent" // QuestionTask is a task proposal: should this work go (task.go). QuestionTask QuestionKind = "task" // QuestionStanding is a standing card: should this be kept an eye on // (tools_standing.go). QuestionStanding QuestionKind = "standing" // QuestionConnect is a connect offer: may one of the person's accounts be // connected, and where the account needs one, the key or address it is // missing (connect.go, [Agent.ResolveConnect] and // [Agent.ResolveConnectKey]). QuestionConnect QuestionKind = "connect" // QuestionHarness is a sub-harness offer or a written design waiting to be // approved (harness.go, [Agent.ResolveHarness]). QuestionHarness QuestionKind = "harness" // QuestionSubharness is an intake card chat raised for a saved program // (tools_subharness.go, [Agent.ResolveSubharness]). QuestionSubharness QuestionKind = "subharness" // QuestionSubharnessAsk is a RUNNING sub-harness putting its own question to // the person (subharness_env.go, [Agent.AnswerSubharness]). The audit found // this lane drawn by nothing at all: the run waited, and no surface in the // product had a door onto it. QuestionSubharnessAsk QuestionKind = "subharness-ask" // QuestionLanding is a landed task's `your call` — work that finished and // that nobody could check (task_audit.go, [Agent.ResolveUnverified], // [Agent.HandUnverifiedToModel] and [Agent.TakeBackDecision]). QuestionLanding QuestionKind = "landing" // QuestionConflict is a branch that would not fasten onto the person's // (task_merge_round.go, [Agent.ResolveConflict]). It is the lane the audit // found with a door and no card anywhere. QuestionConflict QuestionKind = "conflict" // QuestionFuel is an adaptive run standing at its fuel gate // (orchestrate.go, [Agent.ResolveOrchestrate]). QuestionFuel QuestionKind = "fuel" // QuestionAsk is the model's own question, raised through the ask tool. QuestionAsk QuestionKind = "ask" )
type Receipt ¶
type Receipt struct {
// Unbilled is the owned calls whose provider receipts could not be priced.
Unbilled int
// Direct is what the node's OWN calls cost — its turns and the auxiliary
// calls made on its behalf.
Direct float64
// Children is every call made inside work this node started, at any depth,
// including the checks and the repair rounds — and the hands a turn forked,
// which are the node's own answer being worked on in parallel rather than a
// task, and are counted here because they are money the node's books do not
// hold until they come home. It is what those books will eventually hold as
// each piece of work closes, and it is here now.
Children float64
// Calls is the whole subtree's requests, on [Receipt.Folded]'s terms: the
// denominator the total is the sum over.
Calls int
}
Receipt is what one node cost, in the three readings every spend surface in this program wants — and it is ONE OBJECT, computed in ONE PLACE (UsageTree), because the alternative is what issue #269 measured: the card, the roster and the spend place quoting three different costs for the same piece of work, each summing the same rows a slightly different way.
A SURFACE CHOOSES WHAT TO SHOW AND NEVER WHAT TO SUM. The status line's money segment and Settings→Spending's `this one` show Receipt.Folded; /cost prints Direct beside Children under it — readings of one arithmetic, rather than one arithmetic per surface.
THE SPLIT IS THE POINT AND THE FOLDED FIGURE IS THE HEADLINE. The defect the split answers (issue #145) is a conversation whose ambient figure read $2.53 while the tasks it had started were spending $51.05 — the smaller number, alone, for two hours, because a node's money only reaches the node's own books when the child closes. Folded is the honest one; the halves are what makes it auditable rather than a figure that jumped.
func UsageTree ¶
UsageTree is the one place a receipt is computed: what a conversation spent itself, and what the work it started spent.
IT IS EACH CALL ONCE, which is the whole reason it reads the ledger rather than adding a running total of its own. A fold writes no ledger line ([Agent.addFoldedUsage]), so a node's calls are here under the node that made them whether the node is still running or closed an hour ago — and a reader that added this to a conversation's own books would count a closed node twice. The books and this are two readings of the same money, not two quantities to add.
A LINE BELONGS TO THE WORK WHENEVER IT NAMES A ROOT, and to the conversation only when it names the conversation itself. That ordering matters at exactly one point: a conversation whose journal id somehow appeared as a root would otherwise be counted in both halves.
An empty conversation id matches nothing at all rather than everything, which is the honest answer for a surface that does not know which conversation it is in.
type Record ¶
type Record struct {
Entries []DisplayEntry
Earlier []DisplayEntry
Floor int
// Unreadable is what a page must SAY about this reading, and "" for a record
// read whole — which is nearly all of them.
//
// IT IS A FINISHED SENTENCE and not a code or a line number, for the reason
// [SteerMark.Landing] is one: the fact belongs to whoever did the reading,
// and a surface composing the sentence from parts is a second place the
// answer can be wrong. There is at most one, because a reading either stopped
// AT a line or was refused the whole file, never both.
Unreadable string
// Requests is what the record's own request lines say about the work it
// holds — the other half of a page watching a node it has no lane to
// ([RequestBooks]).
Requests RequestBooks
}
Record is one session file read for display: the conversation the model still carries, and the region a compaction pass edited away.
IT IS THE SHAPE THE CONVERSATION ALREADY HAS, said once for a file instead of for an open agent: `Entries` is Agent.Transcript and `Earlier`/`Floor` are EarlierHistory, with the same contract between them — THE RECORD, TOLD ONCE AND WHOLE, IS `Earlier` FOLLOWED BY `Entries[Floor:]`.
The region is here rather than left out because a page that dropped it would be showing a person the pass's own shortened copy of their work as though it were the work: the calls above the marker come back as stubs, and the prose comes back as one folded line. THE LENS MAY LOWER SALIENCE; IT MAY NOT DROP A FACT (docs/design/lens/DESIGN.md).
Earlier is empty, and Floor zero, for every record that was never compacted — which is nearly all of them — and for one compacted by a build that did not write the window's length, where the region cannot be placed and is dropped rather than drawn twice ([replayedSession.earlier]).
func ReadTranscript ¶
ReadTranscript is one session file, at rest, as display entries — the shape Agent.Transcript and Agent.EarlierHistory answer with, for a record this process does not hold open.
func ReadTranscriptBytes ¶
ReadTranscriptBytes is the same reading of a record that arrived as BYTES rather than as a path: the tail a hosted page is handed over the wire (TaskRecord.Journal).
A TAIL OPENS MID-FILE, and every part of the reading already survives that: a half line is skipped, a result whose call is above the cut is a message no entry claims, and a file with no header is a file whose format version nobody declared.
type Remains ¶
type Remains struct {
Said string
Reader string
Acceptance string
Landings []Landing
Checks []CheckRun
// Landed says whether this session has finished ANY unit of work THROUGH A
// TASK. A session that has landed nothing has not finished an ask, whatever a
// reader of its transcript makes of it, and [Steward.Decide] refuses to call
// that done.
Landed bool
// Delivery is frozen from the original ask, never from a worker handback.
Delivery deliveryContract
// Made says this session put work on the deliverable WITH ITS OWN HANDS —
// non-empty regular files it created, or files under the tree WHOSE CONTENT
// STILL DIFFERS from what it was before the session wrote them, no task
// involved.
//
// MADE IS ABOUT CONTENT AND NOT ABOUT PATHS. A path the session wrote is not
// a change the session made: a `git stash`, a revert, an edit that puts a
// file back the way it was all leave the path in the ledger and nothing in
// the tree. Measured (#534's follow-up): the attrs cell edited the file that
// held the fix, stashed it to compare against the baseline, never popped it,
// and finished — the door said `finishing here · what was asked is done` over
// a tree with zero changed files ([Agent.changedInDeliverable]).
//
// A SESSION THAT CHANGED THE DELIVERABLE HAS FINISHED SOMETHING. Landed is a
// reading of the task graph, so a run that did the whole job inline had it
// false over a green tree: one measured cell wrote the fix and a 196-line test
// file, went green on 43 tests, never started a task, and read "nothing has
// been finished yet" at the end of both of its replies — the same first line
// twice, which is the standstill's fingerprint, so it stopped over finished
// work one second after tidying up (#513).
Made bool
// ReaderSaysDone says the mark reader was asked and answered that NOTHING IS
// LEFT — which is not the same as Reader being empty, because that is also
// what silence looks like ([readerLine]).
//
// IT IS THE SECOND OPINION Made HAS TO HAVE. A session's own files are not
// evidence about themselves: what makes inline work count as finished work is
// somebody who is not the writer looking at the session and saying so.
ReaderSaysDone bool
// ReaderUnreachable says the mark reader was asked and the call did not come
// back — a transport fault, an expired window or a nil response. It is not set
// when there was nobody to ask, when there was no digest worth asking about,
// or when a reader answered with something that was not prose.
//
// A READER NOBODY COULD REACH IS NOT A READER WHO DISAGREED. In the measured
// Human-Agent-Society-reef-145-chat cell the reader timed out, the finished
// green inline fix was moved to a task, and that task did the work again for
// nine and a half minutes (#582). This field lets the declared checks stand in
// for that missing second opinion without weakening the witness law anywhere
// a reader was absent or actually named a gap.
ReaderUnreachable bool
// Running names the units of work that are IN FLIGHT — started, or queued
// behind something that is — in the words a person reads them by.
//
// WORK THAT IS STILL RUNNING IS NEITHER DONE NOR A STANDSTILL, and this is
// the field that makes both halves of that sayable. An ask with something
// still moving is not finished, however tidy everything that already landed
// looks; and a run whose unmet set has not changed BECAUSE it is waiting on
// something is not going round in a circle, it is waiting, so the floor under
// carrying on ([Steward.standstill]) must not fire on it.
//
// RUNNING MEANS IN FLIGHT AND NOTHING ELSE. Work that is merely UNSETTLED is
// not the same thing: a unit queued behind a prerequisite that settled short
// will never start, and putting it here would hold that floor open for the
// rest of a run that had already stopped getting anywhere — which is the
// whole reason the field below exists beside this one.
Running []string
// Blocked is the work that will not start, said whole: what each unit is
// waiting on and what became of that. It is PART OF WHAT IS LEFT — a unit
// waiting on something that is not coming is a gap in the ask exactly as a
// failed one is — and it is never a reason to keep carrying on.
Blocked []string
// BaselineRead says the before-reading has LANDED. It runs in the background
// at the start of an unattended run, so a reading assembled in the first
// minutes has no baseline yet — and with none, NO CHECK IS COUNTED AS THIS
// RUN'S OWN RED. Naming a check before anybody knows whether it was already
// failing is the mistake this whole field exists to stop, made in a hurry.
BaselineRead bool
// WasFailing is the checks that were ALREADY RED before this session did any
// work, by the same command names Checks carries. It is meaningless unless
// BaselineRead.
//
// A CHECK IS OURS ONLY IF WE TURNED IT RED. An acceptance that says "the
// existing test suite passes" is written over whatever the project's suite
// does today, and where one test was red before anybody touched anything
// that sentence can never be true: the run reads its own failure in the
// project's, carries on into it, and spends its whole ceiling on somebody
// else's bug. Measured (#513): the attrs cell's acceptance was `tox -e py`
// passes over a suite with one pre-existing failure, and it never stopped.
//
// EMPTY WITH BaselineRead IS A CLEAN TREE ONLY IF Unread IS EMPTY TOO; empty
// without BaselineRead is a reading that has not landed. A session that never
// takes one — every watched session — counts no check as its own, which is
// the one safe answer when nobody knows what was red to begin with.
WasFailing []string
// WasFailingTests keeps failure identities inside baseline-red commands,
// keyed by the exact declared command both readings ran.
WasFailingTests map[string][]string
// Unread is the current checks with no usable before-reading: one declared
// after that reading, one that changed the tree and had its answer thrown
// away, one the shell could not run, one the window never reached.
//
// A CHECK WHOSE READING WAS THROWN AWAY DOES NOT MAKE EVERY RED LOOK NEW.
// With WasFailing alone, a baseline that discarded its ONLY check came back
// empty — which the field above calls a clean tree — over a project with 85
// pre-existing failures, and every one of them then read as this run's own
// (#513: the attrs cell's `python -m pytest tests/` was discarded because
// pytest writes `.pytest_cache/`). So what could not be read is carried
// separately and is never counted either way.
Unread []string
// Stashed is how many entries `git stash list` names in the deliverable tree
// at the terminal reading ([Agent.terminalAudit]). Zero for a tree that is
// not a repository, and zero where there is no git to ask.
//
// A STASH IS WORK THAT IS NOT IN THE TREE, AND IT IS SAID OUT LOUD. Every
// other reading here — the checks, the reconciliation, the session's own
// ledger — reads the tree as it stands, and a tree with the fix stashed out
// of it looks exactly like a tree the fix was never written into. The one
// party that knows better is git, so it is asked, and what it says becomes a
// line in [Remains.unmet] rather than a fact nobody carried: a done cannot be
// decided over it, and the carry-on brief tells the model exactly what is
// wrong instead of sending it to write the fix a second time.
Stashed int
}
Remains is the end of a turn as a principal is shown it.
Reader is the mark reader's one line about what is left. An empty line may be the reader saying the ask is met, nobody being there to ask, or a call that did not come back; the fields below keep apart the facts that the prose cannot. The three readings under it are what a person would have looked at before agreeing: the acceptance for the whole ask, how the units of work landed, and what the session's own declared checks say about the tree right now.
type ReplayCursor ¶
ReplayCursor names a turn within one live agent. Reopening the same journal creates a different owner, so an old snapshot cannot suppress a new engine.
func (ReplayCursor) Covers ¶
func (c ReplayCursor) Covers(other ReplayCursor) bool
type RequestBooks ¶
type RequestBooks struct {
// Latest is the prompt the newest request sent, whole — what that request
// weighed. The providers codeaf speaks to count cached prompt tokens INSIDE
// the prompt figure (an OpenAI-shaped usage block, which is what OpenRouter
// returns); a line whose cached share is LARGER than its prompt can only
// have been counted the other way, and has the two added back together.
Latest int
// Recent is the newest requests in the reading, oldest first, at most
// [requestsKept] of them. It is a LIST rather than a sum because a reader
// of successive tails needs to tell the requests it has already counted
// from the ones that are new: the window slides as the file grows, and a sum
// over it would shrink every time an old request fell off the top.
Recent []RequestLine
}
RequestBooks is what a journal's `call` lines — ONE PER MODEL REQUEST the conversation made, banked beside the seal that sums them ([Agent.addUsage]) — say about the work in the bytes that were read.
IT EXISTS FOR A PAGE WITH NO LANE. A surface watching a node on this machine hears its steps as they end; a surface watching one on ANOTHER machine is handed a bounded tail of its journal and nothing else, and every live figure it draws has to come out of those bytes. The request lines are the only place the provider's own counts are written down per request, so they are read by the same scan that rebuilds the transcript — one record, one reading — rather than by a second parser of the same file.
ONLY THE CONVERSATION'S OWN REQUESTS ARE COUNTED. An errand's call — a title, a memory reflex, a review — is written with its role on it and is not the work's writing ([Agent.journalRoleCall]); counting it would make a page's ↓ jump for a sentence nobody on the page wrote.
THE READING IS BOUNDED BY WHAT WAS READ. A tail opens mid-file, so Recent is the requests in the tail and not the whole run above it. Latest is always there when any request is: the newest line is the last one written.
type RequestLine ¶
type RequestLine struct {
Input, Output int
}
RequestLine is one request as its line spells it: what it sent and what came back.
type RetryNews ¶
type RetryNews struct {
// Model is the model whose attempt just failed — the one that was being
// asked, never the one about to be.
Model string
// Attempt and Attempts are how far into this model's patience the step is:
// `2` of `4`. Attempts is the POLICY's number, resolved from the person's
// settings and from the shape of the failure (internal/taxonomy), never a
// constant a surface holds — which is the only way the row a person reads
// and the budget the loop actually walks stay the same number.
Attempt int
Attempts int
// Reason is why, in the person's own words and never the journal's — "the
// model would not take the request", "the model went quiet mid-reply". The
// shapes are named once in internal/taxonomy and spelled for a person once
// in taxonomy_boundary.go's transportWords; nothing here is a machinery
// word, and a surface may show it as it stands.
Reason string
// Next is the model the step is MOVING TO, and it is empty when the step is
// asking the same model again.
//
// THAT EMPTINESS IS THE WHOLE DISTINCTION a surface could not draw before.
// A hop changes whose weights finish a reply somebody is already reading, at
// a different price and in a different voice; a retry changes nothing except
// that the last attempt is void. Both arrive on this one kind, and this is
// what tells them apart.
Next string
}
RetryNews is one attempt being replaced, in parts.
It rides on every EventRetrying this engine sends and on nothing else. Every field is a short fact a surface may independently ignore, and NONE of them is the sentence: a surface that wants words uses Event.Text, which is written by the same code path and says the same thing.
type RewindPoint ¶
RewindPoint is one place the conversation can be cut. Index is the message index the cut drops from — messages[Index:] go. Turn marks a cut landing on something the person said, the anchors a rewind usually returns to; a false Turn is a step boundary inside a turn (after a tool result or an assistant reply). Said carries the person's words at a Turn cut, for the surface to hand back to the draft. Entry is the index into Agent.Transcript's display entries at which the drop would begin, so a surface can place the cut line on the exact row it drew.
type RunAdmission ¶
type RunAdmission interface {
// MayStart reads host pressure before one worker is launched.
MayStart() bool
// Started reserves the lane after a worker has been built.
Started()
// Returned releases that lane when its worker comes home.
Returned()
// HeldBy names the settings row whose ceiling the last refused start met,
// [config.KeyTaskMaxLoad] or [config.KeyTaskMinFreeMB], and is empty until
// a start has been refused. A headless door has no rail to draw
// `machine busy` on, so it says which limit held it in words instead.
HeldBy() string
}
RunAdmission is the run engine's three admission verbs, and one question a door with no rail asks of it. It uses the same governor and process lane account as the node frontier without exposing the governor's readings across the engine seam.
func NewRunAdmission ¶
func NewRunAdmission(maxLoad float64, minFreeMB int, lanes *TaskLanes) RunAdmission
NewRunAdmission builds the machine gate for either run door. Zeroing both ceilings gives the engine a nil gate, the governor's existing off rule.
type RunAskAnswer ¶ added in v0.4.0
type RunAskAnswer struct {
Text string `json:"text"`
From []RunAskSource `json:"from"`
IsNote bool `json:"-"`
Note string `json:"note,omitempty"`
}
type RunAskExchange ¶ added in v0.4.0
type RunAskSource ¶ added in v0.4.0
type RunCharge ¶
RunCharge is one priced call a run's worker made, as the run metered it: the model that answered, the tokens and the cached share, and what the provider charged — zero where the service reports no price, which is a call nobody could price and never a free one.
type RunEngine ¶ added in v0.4.0
type RunEngine interface {
Start(ctx context.Context, spec RunSpec) RunSummary
Land(ctx context.Context, store *plandb.Store, workspace, base, rootID string) (RunLanding, error)
}
RunEngine is the run engine as this door reaches it. Start drives one store to an outcome and answers what came of it; Land commits the run's working copy onto its branch and answers where the work went.
type RunLanding ¶ added in v0.4.0
type RunLanding struct {
Branch string
Changed []string
Refused string
// Home is how the work came home, in the landing road's own outcome words
// ([mergeMerged] and its kin), set by this door once the run's copy has been
// brought back to its ground ([Agent.landBeltRun]). Empty is an engine's own
// landing, which commits on the copy's branch and merges nothing.
Home string
// Line is a landing that says itself: a program's run, whose folder's
// ending ([ProgramFolderEnd.Sentence]) is the whole account of where its
// work is ([beltLandingLine]). Empty for every other run.
Line string
}
RunLanding is what the run's landing answered: the branch the working copy's work was committed on, the paths that commit carried, and the sentence saying why it refused. A landing names a branch or says what stopped it.
type RunLimit ¶ added in v0.4.0
type RunLimit string
RunLimit is which bound a person set ended a run. The engine's outcome word is one sentence for every limit; it is the exit ladder's own word and the ladder keeps its one rung, so this fact is what says which limit fired. It is set where the run decides the limit was reached and read where the ending is drawn; it is never parsed back out of a sentence.
type RunPlanSummary ¶ added in v0.4.0
type RunSpec ¶ added in v0.4.0
type RunSpec struct {
// Store is the plan the run drives. The door opened it and keeps it open
// for the life of the run; the engine reads and writes it like any other
// writer of the store.
Store *plandb.Store
// Workspace is the directory every worker types in and the landing
// commits: the run's own working copy, or the folder itself for a program
// that edits files (programfolder.go).
Workspace string
// Title and Brief are the run's own words: the title names the root row,
// and the brief is the assignment the root worker reads.
Title string
Brief string
// Slots is how many workers run at once, and 0 is no limit, which is
// the word `task.parallel` itself uses. CostUSD is what is left of the
// smaller dollar limit the person set on the conversation, so the run and
// conversation spend from the same finite allowance.
Slots int
CostUSD float64
// Elapsed is the conversation time still available when this run starts.
// Zero means no time ceiling, matching the run engine's Limits contract.
Elapsed time.Duration
// StepsPerTask is the per-task step cap, the same figure a node of this
// session's own tree carries.
StepsPerTask int
// Admission is the shared machine gate consulted for every worker start.
Admission RunAdmission
// OnHold announces the changed set of task ids whose starts are held.
OnHold func([]string)
// ProfileDir is the person's profile directory, read by the engine's crew
// factory to seat a task on the model its role rides.
ProfileDir string
// Sources is the admitted provider set used by the conversation's calls.
// Workers need the same facts to explain a refusal from the right account.
Sources modelsource.Set
// WorkModel and PlanModel are the two seats the conversation resolved for
// this run: the work seat every leaf rides and the plan seat every planner
// rides. The engine's crew factory seats those two roles on them rather
// than asking the profile again, so the seat a conversation's task runs in
// is the seat the conversation's own ladder says. They are read off the
// conversation's role ladder ([roles.TierModel] through Config.RolesSource)
// — the same rows its own planner and worker calls resolve through — and
// empty means the ladder holds no row for that tier, which falls to the
// profile's tier the way an empty seat always has.
//
// Only these two travel: the careful row a check rides and the small row a
// probe rides are named by no door and stay the profile's.
WorkModel string
PlanModel string
// CheckModel is the checker the task's crew was routed to, and empty when
// the run was not routed — the engine then reads the check seat's own
// environment rung and the profile's checker row, and never the plan
// seat's model.
CheckModel string
// OneModel is the conversation's model when the conversation runs under
// `--one-model` ([Config.OneModel]), and empty otherwise. EVERY SEAT OF THE
// RUN RIDES IT: work, plan, check and the probe no door names. The flag
// withholds the roles ladder and the crew router, so without this the
// engine's crew factory found three empty seats and filled them from the
// profile's crew rows — a run under a flag that promises one model billed
// the crew's.
OneModel string
// CompleterFor answers the provider a worker is seated on. The door hands
// the conversation's own — a run worker's calls go out the way the
// conversation's do — and a nil one lets the engine build each worker's
// client itself.
CompleterFor func(model string) Completer
// Serves answers whether this conversation's services can take a call on a
// model ([ServesModel], read live). A delegated run's model API asks it of
// every model the program names, and answers a model nothing here can
// reach on the run's work seat instead. Nil answers yes for every model.
Serves func(model string) bool
// ModelPrice is the catalog price used to reserve each model API call.
ModelPrice func(model string) (input, output float64, known bool)
// OnSpend observes the reconciled cumulative run spend while work is live.
OnSpend func(float64)
// OnCharge observes each priced call a worker that meters call by call
// makes — a delegated run's program, through its model API — with the
// call's own tokens and model, so the conversation folds the call whole
// rather than as a bare dollar figure ([RunCharge]). Nil for a worker that
// only reports its running total.
OnCharge func(RunCharge)
// Conversation is the id of the conversation that started the run: the
// journal id its own ledger rows carry as their Session. A delegated
// run's ledger rows name it as their Root and their Session, beside the
// task's id, so the conversation's receipt and the spending page can place
// the money. Empty leaves those rows naming no conversation.
Conversation string
// Delegate, when set, is the program this run's root task is handed to
// instead of a bash worker (delegate_door.go). No key goes with it: the
// program reaches a model only through the API codeaf serves the run. Nil is
// every run the conversation's own workers drive.
Delegate *delegate.Delegate
// PlainFolder says the delegated run's program works in its folder without
// git ([ProgramFolder.Plain]), so it is started with its own flags for that
// (delegate.Delegate.PlainFolder). False for every other run.
PlainFolder bool
// ProgramBranch is the program's own branch ([ProgramFolder.Branch]).
// ProgramIgnoredFile and ProgramInputsFile hold the child's recorded
// trees to the ignore rules recorded before it started and to the
// untracked files it changed of those copied in
// ([ProgramFolder.InputsFile]).
ProgramBranch string
ProgramIgnoredFile string
ProgramInputsFile string
// ProgramBriefNote is the line a program working in a copy is told where
// the copy is by, ahead of its brief ([ProgramFolder.BriefNote]).
ProgramBriefNote string
// ProgramFolderHold is the file the run's hold on the program's folder is taken
// on, handed to the program's process ([ProgramFolder.Hold]).
ProgramFolderHold *os.File
// Crew is the conversation's crew as a delegated run's program is handed it
// ([conversationCrew]), so the program works on the models the person
// chose. Zero for every other run.
Crew delegate.Crew
// Standing is the person's standing orders over this place, already
// rendered as the section a worker's brief closes on ([StandingWorld],
// resolved once per run against the conversation's own place). It is the
// same answer a task node starting in this conversation reads
// ([TaskGraph.standingWorld]) — a plan-born worker and a node worker must
// never disagree about what stands (#1549). Empty when nothing stands or
// the ambient side is off: no orders is no section, never an empty heading.
Standing string
}
RunSpec is one run as the door hands it to the engine: the store to drive, the working copy its workers share, the run's own words, the conversation's two limits, and the provider its workers are seated on.
type RunSummary ¶ added in v0.4.0
type RunSummary struct {
Outcome string
Result string
// Limit is empty on every run that did not end on a bound its person set.
Limit RunLimit
// Program is how a delegated run's program ended when it did not finish,
// nil otherwise ([ProgramEnding]).
Program *ProgramEnding
// ProgramVerdict is a delegated run's program's own word for the work it
// FINISHED — senior-dev's `pass` or `pass-unverified` — and empty for
// every other ending and every other run.
ProgramVerdict string
// Cut is every task the run's own ending cut mid-flight, by store id: the
// same typed fact as the limit, read where the run recorded it. A joined
// row in this set is drawn with the run's own ending and never as a fault.
Cut []string
Nodes int
Steps int
USD float64
}
RunSummary is what a run came to, folded onto the words this package reads: the engine's outcome word, the root's result, the run's size, and the limit that ended it when one did.
type RunTreeSnapshot ¶
type RunTreeSnapshot struct {
// contains filtered or unexported fields
}
RunTreeSnapshot is what a working copy held before a run touched it: the commit it stood on and, for every path git already saw as changed, what that path held. A door that runs IN PLACE — `codeaf do`, which edits the directory it was handed and makes no commit of its own — takes one before the run and asks it afterwards which paths the RUN changed, so the files it names are the run's and never the person's own edits that were sitting there first.
THE PERSON'S WORK IS NOT THE RUN'S WORK. A copy's `git status` after a run is the run's changes AND whatever the person had not committed yet, and the landing that staged the whole of it committed somebody's half-finished edit and an untracked secrets file as `task: <title>` on their own branch. The snapshot is how the two are told apart without any ledger: a path the run did not touch holds exactly what it held before.
A directory that is not inside a git work tree has no snapshot to take, and RunTreeSnapshot.Changed answers nothing for it: there is no status to read the run's work off, and a walk of an arbitrary folder is not this door's business.
func SnapshotRunTree ¶
func SnapshotRunTree(dir string) RunTreeSnapshot
SnapshotRunTree reads dir's working copy as it stands now.
func (RunTreeSnapshot) Changed ¶
func (s RunTreeSnapshot) Changed() []string
Changed answers the paths the run changed since the snapshot was taken, as absolute paths, sorted: a path git sees as changed now that was clean before, a path that was already changed and now holds something else, a path that was changed before and is clean now (the run put it back), and every path a commit the run made itself carried. The harness's own files are never in it ([harnessWrote]).
func (RunTreeSnapshot) SignRunCommits ¶
func (s RunTreeSnapshot) SignRunCommits(model string) (int, error)
SignRunCommits signs commits made since this snapshot in an in-place run. A snapshot on an unborn branch can sign the run's first commits; a snapshot outside git has no branch whose new history belongs to the run.
type Seat ¶ added in v0.3.0
type Seat string
Seat is one word of the closed vocabulary a usage row's seat field carries.
const ( // SeatReflex is the cheapest of all: the two calls made every single turn. SeatReflex Seat = "reflex" // SeatLow is the cheap, fast one: a title, a caption, the guardian. SeatLow Seat = "low" // SeatWorker is the tier that DOES THE WORK: a task node's own turns. SeatWorker Seat = "worker" // SeatHigh is the capable, expensive one: the auditor, a repair round. SeatHigh Seat = "high" // SeatMastermind is the tier whose one answer shapes all the others: the // planner, the designer, a division review. SeatMastermind Seat = "mastermind" // SeatJudge is the seat a run's judge is billed to, mapped from no tier // and no role. SeatJudge Seat = "judge" // SeatTalk is a conversation's own turns — the one kind of call no tier // governs, because the person is sitting in it and its model is theirs to // change mid-sentence. SeatTalk Seat = "talk" )
func SeatOfAgent ¶ added in v0.3.0
SeatOfAgent is the seat an agent's OWN turns bill to: chat is talk, task is worker, audit and repair are high. An unknown kind is false, and the row it would have carried stays wordless rather than guessed.
func SeatOfRole ¶ added in v0.3.0
SeatOfRole is the seat a registered role's calls bill to: roles.TierOf followed by SeatOfTier, one hop through the registry rather than a second hand-written table. A role the registry never had is nobody's seat — false, and the row carries no word rather than a guess.
func SeatOfTier ¶ added in v0.3.0
SeatOfTier maps each of roles.Tiers to its own word, and reports false for anything else. No tier maps to talk, because talk is what a conversation's own turns answer under and no tier governs those — and none maps to judge either, because judge is the seat a run's judge is billed to and no tier holds that.
type SessionLockedError ¶
type SessionLockedError struct{ Path string }
SessionLockedError names the file another process holds.
func (*SessionLockedError) Error ¶
func (e *SessionLockedError) Error() string
func (*SessionLockedError) Unwrap ¶
func (e *SessionLockedError) Unwrap() error
type SessionPresence ¶
type SessionPresence struct {
// Schema is [presenceSchema]. It is first in the struct because it is the
// first thing a reader decides on.
Schema int `json:"schema"`
// SessionID is the conversation's id — the same string the journal header
// carries, the session folder is named, and [TaskIndexEntry.SessionID]
// records.
SessionID string `json:"sessionId"`
// Workspace is the REAL workspace path, exactly as [Meta.Workspace] records
// it: the resolved project root, or the owned work/ directory. The encoded
// bucket the folder sits in is not an identity and is not written here.
Workspace string `json:"workspace"`
// Build names the codeaf that is making this live claim.
Build string `json:"build,omitempty"`
// PID is the process holding this session, recorded so a person looking at
// two windows can tell which is which. IT IS NOT CONSULTED FOR LIVENESS:
// pids are reused, and a presence file may be read across a filesystem
// shared by two machines where the number means nothing at all. Age is the
// only liveness rule this file has (see the header).
PID int `json:"pid"`
// UpdatedAt is when this refresh was written, and it is the clock every
// freshness judgement is made against. It is IN THE FILE rather than taken
// from the file's mtime because a copy, a restore or a backup tool can
// move an mtime without the session ever having been alive; the stamp
// travels with the claim it dates.
UpdatedAt time.Time `json:"updatedAt"`
// State is what the session is doing.
State PresenceState `json:"state"`
// Reason is one line about WHY it is waiting, and it is empty for every
// other state and for a question this file has no words for. A surface must
// draw nothing at all when it is empty rather than a placeholder.
Reason string `json:"reason,omitempty"`
// Question is the card behind that line, when the lane that raised it could
// describe one another window may answer. It is the zero value for every
// other state and for a question with no answers to offer — the stuck-turn
// question borrows the consent lane to ask about a TURN (recovery.go), and
// it is deliberately not answerable from anywhere but its own window.
Question PresenceQuestion `json:"question,omitzero"`
// RunningTasks is the work this session has out right now: its task nodes
// in admission order, then its adaptive runs in the order they were minted.
// Nil when there is none, which is most sessions.
RunningTasks []PresenceTask `json:"runningTasks,omitempty"`
// Jobs are the background jobs this session has running right now, in the
// order they were started. Nil when there is none — and on every file
// written by a build older than this field, which reads the same way.
Jobs []PresenceJob `json:"jobs,omitempty"`
// Dir is the session folder this was read from, filled in by the reader and
// never written to the file — the folder already knows where it is, and a
// path recorded inside it would be a second answer to go wrong the day a
// state directory moves.
Dir string `json:"-"`
}
SessionPresence is one live session as another window sees it.
func HomePresence ¶
func HomePresence(exclude string) []SessionPresence
HomePresence is ReadAllPresence over this machine's real state root, which is the same path SweepHome sweeps. It is the one door a surface should need.
func ReadAllPresence ¶
func ReadAllPresence(root string, now time.Time, exclude string) []SessionPresence
ReadAllPresence is every live session on this machine, across every project: one readdir of the projects root and one of each bucket under it. It is the union home is built on, and it opens no journal and takes no lock.
func ReadProjectPresence ¶
func ReadProjectPresence(bucket string, now time.Time, exclude ...string) []SessionPresence
ReadProjectPresence is every live session in ONE project bucket — the directory holding a workspace's session folders — most recently refreshed first.
exclude is the caller's OWN session id, dropped from the answer. Every caller of this has one: a rail drawing "what else is running" must not draw the window it is being drawn in, and making that the caller's business would be making it the caller's bug. An empty exclude drops nothing.
func ReadSessionPresence ¶
func ReadSessionPresence(dir string, now time.Time) (SessionPresence, bool)
ReadSessionPresence reads one session folder's presence, and reports false for every reason there is not one to believe: no file, an unreadable file, a schema this build does not know, a session with no id, and — the case the whole file turns on — a claim older than [presenceWindow].
The session folder's own name is trusted over the id inside the file when the file has none, because the folder IS the session id (place.go's Place.ID).
func (SessionPresence) Fresh ¶
func (p SessionPresence) Fresh(now time.Time) bool
Fresh reports whether this claim is still worth believing at now — see the second law in this file's header.
func (SessionPresence) Holds ¶
func (p SessionPresence) Holds(id string) bool
Holds reports whether this session names one node id among the work it has out at this instant.
IT IS THE JOIN, AND IT IS WRITTEN ONCE. Two surfaces now ask the same question of a presence row — the home page, through SessionRow.Runs, and a session's own roster and history page, through Elsewhere.Runs — and a second loop spelling the same comparison is the second place the two could come to disagree about whether a task is running. The id is trimmed on both sides because it is a handle a person types and a file records, not a number anything does arithmetic on (TaskIndexEntry.ID).
A CALLER MUST ALREADY HAVE DECIDED THIS ROW IS FRESH. Nothing here looks at the clock: ReadSessionPresence refuses a stale file outright, so a row that reached a caller is a row inside the window, and asking again here would be a second freshness rule to keep in step with the first.
func (SessionPresence) NeedsPerson ¶
func (p SessionPresence) NeedsPerson() bool
NeedsPerson reports whether this session is stopped waiting on somebody.
It is a METHOD and not a field, because SessionPresence.State already says so and a bool written beside it would be a second source of truth that could disagree with the word next to it on the same row.
func (SessionPresence) Phase ¶
func (p SessionPresence) Phase(id string) string
Phase answers which of its lives the node with this id is in, in task_contract.go's words, and "" for a node this session does not name, one getting on with the work, and one running under a build that did not write the field (PresenceTask.Phase).
IT IS THE SAME JOIN AS SessionPresence.Holds AND IS SPELLED BESIDE IT for that method's reason: the two answer one question about one row — is this node out, and what is it doing — and a surface asking them of two different loops is a surface that can draw a phase on a row it also calls finished.
type SessionRow ¶
type SessionRow struct {
// ID is the session id, which is its folder's name.
ID string
// Dir is the session folder and Transcript its journal — the path a resume
// is asked for.
Dir string
Transcript string
// Project is the bucket's display name and ProjectDir its path, carried on
// the row so that a flattened list still cites where a hit came from.
Project string
ProjectDir string
// Title is the name the session settled on, and "" for one nothing ever
// named. A surface derives a readable name; this layer does not invent one.
Title string
// Workspace is the tools root recorded for the conversation, and Owned marks
// the session whose workspace is its own work/ directory.
Workspace string
Owned bool
// Model is what it was last on.
Model string
// At is when the PERSON last spoke, which is the ordering law everywhere in
// this codebase (place.go's [Meta.LastUserAt]) and deliberately not the
// file's modification time.
At time.Time
// Created is when the folder was minted.
Created time.Time
// Open reports that a window is holding this journal AT THIS INSTANT. It is
// the kernel's answer and not a file's claim — see this file's header.
Open bool
// Presence is what the conversation SAYS it is doing, and Live whether it
// said so recently enough to be believed ([SessionPresence.Fresh]). The
// pair is deliberately not collapsed into one nullable value: a surface asks
// "is this alive" far more often than it asks what the claim was, and Live
// is the whole of that question.
//
// A live conversation and an OPEN one are not the same fact. Open is a lock
// held; Live is a session refreshing a file and naming what it has out. A
// build older than presence.json is open and not live, which is exactly the
// case this layer degrades for rather than lies about.
Presence SessionPresence
Live bool
// Spend is what THE CONVERSATION ITSELF has cost — the turns and the
// auxiliary calls beside them — and Tokens what it weighed, input plus
// output as one sum. Both are read off meta.json, which the session stamps
// at the end of every turn (placemeta.go), so this layer answers them
// without opening a single transcript.
//
// THEY ARE NOT [TaskRollup.Spend] AND MUST NOT BE ADDED TO IT HERE. This is
// the talking; that is the work the talking commissioned, and the two are
// counted in two different files by two different writers. A surface that
// wants the whole bill adds them where it draws it, and says so.
//
// Zero is "nobody could say" — a conversation held under a build older than
// the stamp, or one that has not finished a turn — and under the emptiness
// law a surface draws nothing for it.
Spend float64
Tokens int
// Tasks is what the project's index says this session ran.
Tasks TaskRollup
// Places are the folders this conversation turned out to be ABOUT beyond
// the one it is standing in, newest first, exactly as places.go accrued
// them onto the meta. Home draws them; nothing here weighs them.
//
// A MISSING FIELD IS EVERY CONVERSATION TODAY. The set arrived on meta.json
// additively (place.go's [Meta.Places]), so a conversation held under an
// older build has none and a surface draws nothing for it — which is the
// emptiness law and not a conversation about nowhere.
Places []PlaceRef
// Archived says the person put this conversation away from home's resting
// list ([Meta.Archived]); home gathers such rows under one folded line.
Archived bool
// ArchivedTasks is the person's per-task visibility choice from metadata.
ArchivedTasks map[string]bool
}
SessionRow is one conversation as the world sees it: its identity from meta.json, whether a window is holding it right now, and what the project's index says it ran.
func (SessionRow) Doing ¶
func (r SessionRow) Doing() string
Doing is the word the conversation uses for itself — `working`, `waiting on you`, `idle` — and "" for one that is not live.
IT IS THE PRESENCE FILE'S OWN WORD AND NOT A TRANSLATION OF IT. The states are already written in the words a person would use (taskpresence.go), and a surface mapping them to a second vocabulary would be the one place the two could come to disagree about what a session is doing.
func (SessionRow) NeedsPerson ¶
func (r SessionRow) NeedsPerson() bool
NeedsPerson reports that this conversation is stopped waiting on somebody. It answers false for a conversation that is not live at all, because a claim nobody has refreshed is not a claim about now — a window killed while a question was on screen is not still asking it.
func (SessionRow) Phase ¶
func (r SessionRow) Phase(entry TaskIndexEntry) string
Phase is which of its lives one running row of the index is in — the worker, the check, a repair round, the reading that sizes the work — in the words task_contract.go exports, and "" for a row that is merely working, one that has landed, and one nothing alive can say anything about.
IT IS A LADDER OF TWO, AND THE ORDER IS THE POINT. TaskIndexEntry.Phase is filled by the process that HOLDS the graph and travels nowhere (task_index.go), so it is the right answer for this window's own work and empty for everybody else's; the presence file is what crosses a window (PresenceTask.Phase), and it is asked second so a session's own rows never take the slower answer.
A ROW NOTHING IS BEHIND SAYS NOTHING. A conversation whose presence has gone stale is one no live claim exists for, and a phase read off its last file would be this surface narrating a minute that ended when the window did — the same judgement SessionRow.Runs makes one method up.
func (SessionRow) Reason ¶
func (r SessionRow) Reason() string
Reason is the one line behind a question this conversation is stopped on, and "" whenever there is not one — which a surface draws as nothing at all rather than as a placeholder.
func (SessionRow) Runs ¶
func (r SessionRow) Runs(entry TaskIndexEntry) bool
Runs reports whether one row of the project's index is work that is HAPPENING rather than work the file merely remembers starting.
IT IS THE ONE PLACE THAT JUDGEMENT IS MADE. [rollUp] counts with it and every surface drawing a word beside a row asks it, so a screen can never say `running` on a row the count called incomplete. See this file's header for the rule itself; the ladder is: a live conversation's own list of what it has out, then — for a conversation too old to keep one — the lock.
type SpendDay ¶
type SpendDay struct {
// contains filtered or unexported fields
}
SpendDay is today's spend as this process knows it: what the ledger said when the process looked, and every guarded call since.
func NewSpendDay ¶
NewSpendDay is a day that had spent base when it was read.
type SpendGuard ¶
type SpendGuard struct {
Price SpendPrice
Day *SpendDay
// Cap is the day's limit in dollars and CapAction the sentence a call it
// stops ends on.
Cap float64
CapAction string
// SeatCeilings are a seat's own spend ceiling on this task, and
// CeilingAction the sentence (with the ceiling's dollars) a call it stops
// ends on.
SeatCeilings map[crewroute.Seat]float64
CeilingAction string
// TaskCap is the most the task may spend in dollars, across every model
// this guard prices, and TaskAction the sentence a call it stops ends on.
// Zero is no per-task limit.
TaskCap float64
TaskAction string
// Task is the tally TaskCap is read against. Guards that share one hold
// the seats and the helpers of one task to one limit; nil is a tally of
// the guard's own, made on its first call.
Task *SpendTask
// contains filtered or unexported fields
}
SpendGuard holds a task's seat calls to the day's cap and to each seat's own ceiling. The zero parts are off: no Day or no Cap is no day cap, and a seat with no SeatCeilings entry has no ceiling of its own.
func CrewSpendGuard ¶
func CrewSpendGuard(profileDir string, d crewroute.Decision, withDaily bool) *SpendGuard
CrewSpendGuard is [crewSpendGuard] for a headless run's seats.
func TaskSpendGuard ¶
func TaskSpendGuard(profileDir string) *SpendGuard
TaskSpendGuard is the guard a headless run with no routed crew is held to: the per-task limit alone.
type SpendPrice ¶
SpendPrice is a model's price per token: prompt, completion and cache read. ok is false for a model whose price nobody knows; a line already reached still stops it. A known price of nothing is never stopped.
type SpendTask ¶
type SpendTask struct {
// contains filtered or unexported fields
}
SpendTask is what one task has spent and holds in flight, across every guarded call made for it.
type Stakes ¶
type Stakes string
Stakes is what an answer costs if it turns out wrong. It, and not the kind, is what decides whether anything may answer on a clock.
const ( // StakesReversible is work that can be undone with nothing lost but time. StakesReversible Stakes = "reversible" // StakesCostly is work that can be undone but not cheaply — money spent, // an hour of a run. StakesCostly Stakes = "costly" // StakesIrreversible is work that cannot be taken back: something sent, // something deleted, something published. IT NEVER RUNS ON A CLOCK AND // NOTHING EVER ANSWERS IT BUT A PERSON, and [Question.Check] refuses a // question that says otherwise. StakesIrreversible Stakes = "irreversible" )
type StaleBuildRow ¶
StaleBuildRow names one live session whose presence claims a build rev other than the process reading it now. It carries the session id, the workspace it was opened with, the build stamp, and the pid the owner wrote in — reported for a person's `kill`, NEVER for liveness (the header's own law: age is the only liveness rule this file has).
func SweepStaleBuilds ¶
func SweepStaleBuilds(projectsDir, currentRev string, now time.Time) (rows []StaleBuildRow)
SweepStaleBuilds is the launch gate's one question: which FRESH presence rows hold a build whose leading rev token is not `currentRev`. Files that are unreadable, unparsable, empty of Build, or older than [presenceWindow] are invisible to the sweep, by the same three rules the reader above has.
The rev comparison's one rule: the first space-separated token of presence.Build is the rev (buildinfo stamps it "<rev> built <time>", optionally "(dirty)" beside the rev). A row whose build has no token at all still reads as different — it was written by something, and the process reading it is certainly another thing.
type Standing ¶
type Standing struct {
Store *standing.Store
// Watch is this machine's own scheduler, and BACKGROUND CHECKS ARE ON BY
// DEFAULT: the first item that ever stands installs the timer without
// asking anybody, and the conversation says one dim line saying so and
// where to turn it off. Nobody is asked because the question had one
// sensible answer — something you asked to happen every morning is
// something you asked to happen on the mornings you do not open a terminal
// — and the switch lives in /settings under `background checks`
// (internal/config's KeyStandingBackground).
//
// Nil means there is no timer on this host (a remote engine, a test, an
// operating system the package cannot arrange one for), and then nothing is
// installed and nothing is said.
Watch standing.Watch
// DailyRailUSD is the one machine-wide allowance quoted on cards when the
// person named no per-item money. Zero means the allowance is unlimited, so
// the card names the shared allowance without inventing a figure.
DailyRailUSD float64
// DailyRail reads the current allowance when a proposal is made. The
// surface quotes that snapshot without reading configuration on each frame.
// Nil retains DailyRailUSD for embedders with a fixed allowance.
DailyRail func() float64
}
Standing is the seam every door sets so a conversation can propose, list, pause and stop items, and so firings can reach it. Nil means the ambient side is off: the belt tool is absent and no card is ever drawn — a capability that cannot work is absent, not broken.
type StandingAnswer ¶
type StandingAnswer struct {
// Approved stands the item up as proposed, or as changed below.
Approved bool
// Once says "do it once, not standing": the action runs now as an
// ordinary turn or task and nothing is created.
Once bool
// Change is the person's free-text correction — "make it 8pm", "every
// weekday" — which goes back to the model to re-propose. Nothing is
// created on a change.
Change string
}
StandingAnswer is what the person said to a card.
type StandingChange ¶
type StandingChange struct {
// Folder is the absolute folder, Name is its basename — what the chip says,
// because a person recognises `agentfield` faster than a path they would
// have to read to the end.
Folder string
Name string
// Files is how many distinct files are waiting.
Files int
}
StandingChange is one folder with work in it that has not landed — what the composer's chip draws, and nothing more. A conversation with nothing waiting answers an empty slice, and the surface draws nothing at all.
type StandingNotice ¶
type StandingNotice struct {
ID uint64
// Item is the proposed item, complete, as it would be created on a yes:
// the person's words, when, what it does, the rails. A surface draws it
// and never reshapes it; what the person changes comes back in the answer.
Item standing.Item
// WhenWords and CostWords are the two sentences the card leads with, in
// the model's own words at proposal time: "Mondays at 9am", "about $0.02 a
// run, at most once a day". The surface quotes them; it does not compute
// them from the spec, because a spec read back as cron is a spec nobody
// can check.
WhenWords string
CostWords string
// Guessed says the model invented the cadence because the person gave
// none, and the card should ask rather than state: "about every 2 minutes
// — you didn't say, so that's my guess. Right?"
Guessed bool
// Options are the answers THIS card offers, from [StandingOptions].
//
// THE ENGINE SAYS WHICH CHIPS A CARD HAS, so the conversation's card, home's
// answer row and the keys this session will actually take are one decision
// made once. A one-off reminder offers no `once`, because doing "say time to
// leave" NOW is not a smaller version of doing it at six — it is a different
// thing, and usually nothing. A surface reads this rather than reasoning
// from the item itself; the zero value means the kind's full row
// ([AnswerOptions]).
Options []AnswerOption
// Deadline is ALWAYS ZERO from this build, and a surface draws no meter
// for a zero. A standing card is read by a person, and a card that ended
// itself while they were reading it was never answered — so the wait ends
// on their answer, on an interrupt, or on the session closing, and never on
// a clock (tools_standing.go). The field stays because the event's shape
// does.
Deadline time.Time
// Update is set on EventStandingUpdate: "stood", "fired", "paused",
// "resumed", "stopped", "needs-you", "failed". Empty on a proposal.
Update string
// Text is the one line an update carries: what it said, what it landed,
// what it is stopped on.
Text string
}
StandingNotice is the card. It is the payload of EventStandingProposal (ID is the token a surface hands back to Agent.ResolveStanding) and of EventStandingUpdate (ID is zero; Item is the item as it now stands).
type StandingTree ¶
type StandingTree struct {
// Folder is the referred folder this copy is OF: absolute, canonical, and
// the repository root when the folder is inside one, exactly as
// [PlaceRef.Path] is.
Folder string `json:"folder"`
// Dir is the working copy itself, under the session's own trees/.
Dir string `json:"dir"`
// Mode is how this copy stands on the folder — [TaskModeWorktree] or
// [TaskModeMirror] — and it is the task modes' own vocabulary rather than a
// second one, because the landing it takes is the task landing.
Mode TaskMode `json:"mode"`
// Branch and Root are the worktree's half: the branch the work is on and
// the repository it merges back into. Both are empty for a copy.
Branch string `json:"branch,omitempty"`
Root string `json:"root,omitempty"`
// Home is the root checkout's branch when this conversation cut its branch,
// so a later /land cannot follow a checkout that moved underneath it.
Home string `json:"home,omitempty"`
// HomeSha is the commit Home named at that cut, so /land can tell the same
// branch moving to a different world from the branch staying where it was.
HomeSha string `json:"homeSha,omitempty"`
// Cut is when the copy was made, which is the moment everything in the
// folder was still true.
Cut time.Time `json:"cut,omitempty"`
// Wrote is what THIS CONVERSATION'S OWN HANDS wrote, folder-relative and
// slash-spelled, in the order it was written and without repeats.
//
// IT IS THE ONE READING OF WHAT LANDS, for [taskTree.comeHome]'s stated
// reason: a landing that walked the directory instead would carry back
// everything a build left in it. It is also what the chip counts, so the
// number a person sees and the files that move are one list.
Wrote []string `json:"wrote,omitempty"`
}
StandingTree is one conversation's own working copy of one referred folder: where the folder is, where the copy is, and what has been written into it that the folder itself does not have yet.
IT IS A RECORD AND NOT A CITATION, which is what makes it different from everything else on Meta. A referred place is recoverable by looking at the disk again; unlanded work is not recoverable from anywhere, so this is written down the moment it changes and read back at open — a person who closes the terminal with work in a copy finds it waiting.
type SteerMark ¶
SteerMark is what the record keeps about one steer that LANDED: when the person sent it, and — always true here — that the turn it was typed into carried it to the model.
Consumed is a field rather than an assumption because the record has to be able to say both things, and because a reader of a session file finds the other answer written next to these same words ([journalSteer]).
type SteerNote ¶
SteerNote is one steer as the three steer events carry it: which one it is, what was said, and when the person sent it.
The ID is this session's own counter and not a provider's anything. It exists so a surface can pair an outcome with the row it drew on the acceptance without matching text — two identical corrections typed a second apart are two steers, and a surface that paired them by words would resolve the wrong row.
type SteerReceipt ¶
type SteerReceipt struct {
// Waiting says the node had handed its work out and parked on the reports, so
// this line is what wakes it. It is the bool this receipt grew out of.
Waiting bool
// Held says nobody was inside the node to read the words and the work is not
// over: they are on the node's record, and the landing revalidates against
// them before anything is published.
Held bool
// Direction is the id of the receipt written on the node's record, and 0 when
// no record was written. It is what a worker cites to revise the assignment
// (assignment.go) and what a surface can pair an outcome with later.
Direction uint64
// Again says this task already held these words, from the same message of the
// person's, so nothing was sent a second time and Direction is the receipt it
// was written down as the first time.
//
// ONLY A SEND THAT CARRIES AN IDENTITY CAN ANSWER IT — a forward from the
// conversation (task_forward.go) or a room's send under a [SteerSource]
// (task_room.go's [Agent.SteerTaskFrom]). Saying the same sentence into a room
// twice is saying it twice and is delivered twice: what is recognised is the
// SEND and never the words.
Again bool
// Landing is the engine's own sentence for what happened, drawn verbatim.
Landing string
// Heard is WHEN the words reach the work, by the same reading a model pick
// gets ([ModelLanding], steer.go): [ModelLandsNow] when the request in flight
// had put nothing in front of anybody and was let go of, so the very next
// request carries this line; [ModelLandsNextRequest] when an answer was
// already arriving and is being allowed to finish, or when there was no
// request out at all.
//
// IT IS THE SAME TYPE AS THE PICK'S ON PURPOSE. A person's word is one rule
// with one clock, and a surface that had to learn a second vocabulary for
// `continue` would be a surface that could say two different things about one
// law.
Heard ModelLanding
}
SteerReceipt is what sending a line to a node DID, as one value that every surface — this process's own and a client at the other end of the wire — reads the same way.
It replaced a bare bool because there are now THREE outcomes and not two, and the third one cannot be an error. A line said while the gate is reading the work is TAKEN: it goes onto the node's record and the landing may not publish over it (assignment.go). Reported as an error it would have been a success the caller had to recognise by matching a sentinel, which the local surface could just about do and a hosted one could not — an error crossing the wire arrives as text, so the fact would have been lost exactly where the person is furthest from the work.
type SteerSource ¶
type SteerSource struct {
// Scope names one life of one surface. Empty is a send with no identity,
// which is exactly what [Agent.SteerTask] has always been.
Scope string
// Seq counts that surface's sends, from 1.
Seq uint64
// At is when the person pressed enter, which is what the node's record
// orders its corrections by — the send may be retried minutes later, and the
// instant that matters is the one they said it at, not the one it landed on.
At time.Time
// Conversation is the session file the surface believed it was addressing.
//
// A TASK NUMBER MEANS SOMETHING ONLY INSIDE ONE CONVERSATION, and a surface
// can hold a send across a swap — /resume and /new replace the conversation
// under a handle that does not change (cmd/codeaf's chatv3_host.go keeps the
// same remote agent). Carried here, the claim is checked where the delivery
// happens; empty is a caller making no claim, and is checked against nothing.
Conversation string
}
SteerSource NAMES ONE SEND, so that a surface which never heard the answer to it can ask again without the worker being told the same thing twice.
THE PROBLEM IT ANSWERS IS NOT DUPLICATE TYPING. A person presses enter, the words cross to the engine, the engine takes them — and the answer is lost on the way back, because the link died, the deadline ran out, or the window was closed and reopened. The surface then holds a sentence it cannot say arrived and cannot say did not, and both of the things it can do are wrong: send it again and the worker reads one correction twice, drop it and the person's words are gone. With an identity on the send there is a third answer — ask again with the same one — and [taskAssignment.hear] recognises it, answers the receipt already on the record and delivers nothing (SteerReceipt.Again).
IT IS THE SEND'S NUMBER AND NEVER A FINGERPRINT OF THE WORDS, which is [personSourceID]'s own law and matters more here than anywhere: the same sentence typed twice into a room IS two corrections — "try it again" after a failure means something the first one did not — so two intentional sends of one sentence carry two Seqs and are two directions, while one send asked twice carries one Seq and is one.
AND THE SCOPE IS WHAT KEEPS TWO LIVES APART. Seq is only meaningful inside it: a surface counts its own sends from 1, so a scope shared with yesterday's window would let tomorrow's first send be recognised as a direction this task already holds and dropped. A surface mints one random scope per life and never persists it (internal/tui3's steersend.go).
type Steward ¶
type Steward struct {
// contains filtered or unexported fields
}
Steward is the principal of an unattended session: it holds the ask, an acceptance written for the whole of it, and a budget, and it answers on the absent person's behalf out of evidence rather than opinion.
EVERYTHING IT DECIDES IS DECIDED FROM FACTS THE SESSION ALREADY HAS. It never calls a model: the reader's line, how the units landed, and what the declared checks say are gathered by the caller and handed over (Remains), and this type is the policy over them. That is what makes every one of its answers testable without a network, and it is why the loop guard can be trusted — a guard that had to ask a model whether two failures were the same failure would be a guard that fails open on a bad evening.
func NewSteward ¶
NewSteward builds the principal of an unattended session.
spent reads the session's accumulated cost in US dollars and MAY BE NIL, in which case no money is ever counted against the ceiling — which is honest rather than convenient: a build with no cost figures must not stop a run on a number it invented.
func (*Steward) Acceptance ¶
func (*Steward) Decide ¶
Decide is the end of a turn, answered on the absent person's behalf.
THE ORDER IS THE POLICY, and it is an order over EVIDENCE:
- A GUARD THAT HAS FIRED OUTRANKS EVERYTHING. Once the same failure has come home [stewardRepeats] times the session is over, whatever a reader says.
- AN EXHAUSTED BUDGET STOPS, and it stops with a report rather than with silence — a run that spent its hours and said nothing is a run nobody can learn from.
- WHAT LANDED AND WHAT RAN COME BEFORE ANY READER'S LINE. This is the rung that moved, and it is the whole of #468. A reader's line is a reading of the TRANSCRIPT — what the session said about itself — while a settled landing and a check that ran are readings of the work. So the unmet set is taken first, and a session whose units of work are done and whose checks all passed is FINISHED, however much a reader still has to say about it. The measured run had a task merged home with twenty-two checks green, and was carried on past it for the rest of its wall on a line somebody's sidecar wrote about the transcript.
- WITH SOMETHING GENUINELY LEFT, THE ADMITTED UNMET FACTS ARE THE BRIEF. A reader's words enter those facts for inline work, where there is no task landing to outrank them; they cannot replace an independent task, delivery or check fact with a fresh obligation.
- WORK STILL IN FLIGHT IS NEITHER OF THE TWO ENDINGS. An ask with a unit of work still going is not finished, and it is not going round in a circle either — it is waiting, so the floor below is not asked about it and what it remembers is dropped (Remains.Running).
- AND THE SAME THING TWICE RUNNING IS A STANDSTILL, not a third go ([Steward.standstill]).
THE FROZEN DONE-CONDITION IS NEVER EVIDENCE HERE. It is written before any work happens, out of the ask alone, and it reaches the decision only as the context a brief opens with ([stewardBrief]) — a sentence the session wrote for itself is not a reading of anything.
func (*Steward) Report ¶
Report turns one landing into the next attempt's brief, and it is the road a failed unit of work now has instead of a sentence addressed to nobody.
THE AUDIT'S OWN REPORT IS THE BRIEF. Whoever read the work wrote down what it did and what stopped it; that is already the most specific account of the gap anybody in this session has, and re-deriving it from a fresh model would be paying to be told the same thing less accurately.
A LANDING THAT FINISHED PRODUCES NOTHING. There is nothing to open on the strength of a unit that did what it was asked, and a Steward that briefed one anyway would be a session that never runs out of work to do.
AND THE GUARD COUNTS BEFORE IT ANSWERS. Three landings with one signature (see [stewardRepeats]) stop the session for good, with the report as the thing a person reads. An unsigned landing is not counted at all — a guard that folded every unsigned failure into one bucket would stop a session that was making progress on three different problems.
type StopDoor ¶
type StopDoor string
StopDoor is what ended a turn, in machine words. It is a small closed set on purpose: a door that is not on it is a door nobody can autopsy.
const ( // StopByPerson is the stop key and nothing else: the person asked. StopByPerson StopDoor = "person stopped" // StopByManager is a team manager's team_stop reaching this member's own // session (team_wakewatch.go): the person's Stop in every respect but who // asked, so the member's conversation says the manager did it. StopByManager StopDoor = "stopped by its manager" // StopByTakeover is another window taking this conversation over // (takeover.go). The turn dies where it stands, mid-reply or not. StopByTakeover StopDoor = "taken over" // StopByLeaving is the conversation going away under the turn — a tab // closing, a switch to another conversation, `/new`, filing an exchange. StopByLeaving StopDoor = "conversation left" // StopByClosing is the whole session shutting down. StopByClosing StopDoor = "session closed" // StopByAbandoned is [Agent.Abandon]: a stop that was not let go of in // time, so the session moved on from the turn (abandon.go). StopByAbandoned StopDoor = "abandoned" // StopByWorkStopped is [Agent.StopWork]: everything this conversation had // running was stopped at once (stopwork.go). StopByWorkStopped StopDoor = "work stopped" // StopByRetired is a hosted session let go of for want of anybody attached // to it (internal/remote). StopByRetired StopDoor = "session retired" // StopByEngineStopped is the host going away under a window that is still // in the room: `codeaf engine --stop`, a signal, a stale build retiring // itself. It is not the unattended door — somebody was there. StopByEngineStopped StopDoor = "engine stopped" )
type SubharnessCard ¶
type SubharnessCard struct {
Manifest exec.Manifest
Fields []SubharnessField
// Missing is the names of the REQUIRED fields still blank, in the card's own
// field order. An empty Missing is a card that could be confirmed as it
// stands.
Missing []string
// Why is chat's one line about why this subharness was raised — "the brief
// and a failing test name are both here". It is empty on the `/subharness`
// path, where the person chose it themselves and needs no reason given back
// to them.
Why string
}
SubharnessCard is the intake card, and it is ONE CARD FOR BOTH INTERACTIVE DOORS — the one chat raises when it proposes a match, and the one `/subharness` opens on a name. Every input field appears; the filled ones are stated, the required blanks are highlighted; the person edits inline or answers chat's batched questions, and confirming launches.
GROOMING IS INFER-THEN-CONFIRM, NEVER INTERROGATE. The card is where that law becomes visible: what could be derived from the conversation is already in the fields, and the only thing anybody is asked about is what is in Missing.
type SubharnessField ¶
type SubharnessField struct {
// Field is the schema's own account of it — name, type, title, description,
// whether it is required, what it defaults to.
Field exec.Field
// Value is what has been filled in, in its own JSON. Nil is a blank, which
// the card draws as nothing.
Value json.RawMessage
// Filled says somebody or something actually put this here. It is separate
// from a non-nil Value because a field carrying its schema DEFAULT is
// answered without having been filled, and the card draws the two
// differently: a default is dim, an answer is not.
Filled bool
}
SubharnessField is one line of the intake card: a field of the input schema, and what is in it so far.
type SubharnessMemory ¶
type SubharnessMemory interface {
// Remember keeps one note in the named subharness's own memory.
Remember(ctx context.Context, subharness, note string) error
// Recall reads it back. An empty query is everything it has kept.
Recall(ctx context.Context, subharness, query string) ([]exec.Note, error)
}
SubharnessMemory is where a subharness keeps what it has learned about its own domain — its file in its own bundle, never this conversation's memory. A program that has learned that this company's brief always arrives as a PDF has learned something about its work and nothing about the person it is talking to, and the two must not share a page.
IT IS A SEAM AND THE STORE LANE FILLS IT (docs/SUBHARNESS-CONTRACT.md §6: the stores, the versions and the journals beside each bundle are all that lane's). NIL IS A BUILD WITH NO BUNDLE MEMORY, and the two doors then answer [exec.NotWired] for their own names — which is the contract's own answer for a door with nothing behind it, and is what [exec.UnwiredEnv] does.
type SubharnessRow ¶
type SubharnessRow struct {
Manifest exec.Manifest
// LastRun is the dim note under the row, in a person's words: when it last
// ran and how it went. IT IS EMPTY WHEN THERE IS NO HISTORY and the row
// draws nothing there — never "0 runs", never "never run" (the emptiness
// law).
LastRun string
}
SubharnessRow is one line of the `/subharness` list.
It carries the MANIFEST WHOLE rather than a handful of copied fields, and that is the one-source-of-truth law reaching a surface: the row's name, its one line, its cost shape and its provenance mark are all the manifest's own, so a list and a card drawn from the same registry can never disagree about what a subharness is. What this type adds is the one thing the manifest cannot know — what happened last time.
type SubharnessRunNote ¶
type SubharnessRunNote struct {
At time.Time
Finished bool
// Why is [exec.RunResult.Incomplete] carried verbatim — the sentence was
// written by whoever knew what ran out, and nothing between there and the row
// is entitled to rephrase it.
Why string
CostUSD float64
}
SubharnessRunNote is what a finished run tells the store about itself, so the next `/subharness` list can draw a note under its row.
IT IS THE FACTS AND NOT THE SENTENCE. When it ran, whether it finished, why not where it did not, and what it cost — and no rendering of any of them, because the emptiness law, the word for an unfinished run and how a cost is drawn are all the SURFACE's to decide. A store that rendered them would be a second place those three decisions are made.
type SubjectKind ¶
type SubjectKind string
SubjectKind says what sort of thing a question is ABOUT, so a surface can find the row it already drew for it.
const ( // SubjectNone is a question about nothing already on screen. The head and // the attached blocks are the whole of what a person has to read. SubjectNone SubjectKind = "" // SubjectCall is one tool call, named by [SubjectRef.CallID]. SubjectCall SubjectKind = "call" // SubjectNode is one task node, named by [SubjectRef.ID]. SubjectNode SubjectKind = "task" // SubjectPage is a written page — a harness design — named by // [SubjectRef.Name]. SubjectPage SubjectKind = "page" // SubjectRun is an adaptive run, named by [SubjectRef.Ref]. SubjectRun SubjectKind = "run" // SubjectOrder is one standing order waiting to be agreed to, named by // [SubjectRef.ID]. It is the card the transcript is already drawing — the // person's own sentence, when it wakes, what it costs and how far it reaches // — so a surface that finds it says the question's reason once rather than // under both. // // IT IS `order` AND NOT `standing` because [SubjectStanding] is already // taken, by the spend ledger, for the thing money was spent inside // (usage_spend.go). Two names for two ideas. SubjectOrder SubjectKind = "order" // SubjectAccount is one of the person's connected accounts, named by // [SubjectRef.Ref] and read out in [SubjectRef.Name]. SubjectAccount SubjectKind = "account" )
type SubjectRef ¶
type SubjectRef struct {
Kind SubjectKind `json:"kind,omitempty"`
// ID is the numeric token where the subject has one — a task node's id.
ID uint64 `json:"id,omitempty"`
// CallID is the tool call's own id, as EventToolBegin and
// EventConsentRequest both carry it.
CallID string `json:"callId,omitempty"`
// Ref is the string token where the subject's id is a string — a connect
// account, an adaptive run.
Ref string `json:"ref,omitempty"`
// Name is the word a person reads for it, and never an id spelled out.
Name string `json:"name,omitempty"`
}
SubjectRef names the row a question is about, AND THE ROW IS DRAWN ONCE.
That bound is consent.go's own law repeated here for every lane: two renderings of one call is how a person ends up approving something other than what they read. A question points AT the row a surface has already put on screen; it does not carry a second copy of it.
type SubjectSpend ¶
type SubjectSpend struct {
// Kind is one of [SubjectTask], [SubjectStanding], [SubjectConversation].
Kind string
// ID is the thing's own id — a node's id within its session, a standing
// item's id, or the conversation's 16 hex. It is what a page JOINS on to get
// a title: this file has no titles and will not invent any, so a page reads
// the name off the task index, the standing store or the session it is
// already holding.
ID string
// Session is the journal the calls were made under — which for a piece of
// work is THE NODE'S OWN transcript and not the conversation that asked for
// it, exactly as [UsageLine.Session] is. It is the same value as ID on a
// conversation row.
//
// It said "the conversation a task's work was journaled under" until issue
// #168, which was never true of a node and misled nobody only because
// nothing joins on it: a page holding an ID finds the task index row by that
// ID alone. The conversation a piece of work belongs to is [UsageLine.Root].
Session string
// Root is THE CONVERSATION THE WORK BELONGED TO ([UsageLine.Root]), and it is
// the half of a task's identity that Session is not.
//
// A TASK ID IS NOT UNIQUE AND THE PAIR THAT IDENTIFIES ONE IS (id,
// conversation) — [TaskIndexEntry.ID] says so, and the index's own
// [TaskIndexEntry.SessionID] is that conversation. Session here is something
// else entirely: the ledger writes the task node's OWN journal id into it,
// so a page joining Session against the index matched nothing on real data
// and fell back to the id alone — which opens whichever conversation's task
// `7` the reader happened to walk first.
//
// It is empty on a row that is not a task, and on a task line written before
// this field was read, where the id alone is all there is.
Root string
// Workspace is the project the money was spent against, and empty where the
// line named none.
Workspace string
// Label is the row's KIND WORD and nothing more: "task", "standing", "chat"
// ([UsageSubjectWord], which says why they are that short). It is
// deliberately not a title — see ID — and it is here so that the three
// spellings live in one place rather than in each page that draws them.
Label string
Calls int
Tokens int
USD float64
}
SubjectSpend is one row of "what it was for": which thing, and what it cost.
func UsageBySubject ¶
func UsageBySubject(lines []UsageLine) []SubjectSpend
UsageBySubject groups the lines by what they were spent on, dearest first.
A STANDING FIRING IS A STANDING FIRING AND NOT A TASK, even though it runs with a node's id beside it: a person recognises the promise they made months ago long before they recognise the run it spawned this morning, so the standing id wins wherever a line carries both. After that a task id wins over a conversation, for the same reason — the work is the thing that was asked for.
AND WORK WITH NO ID OF ITS OWN BELONGS TO THE CONVERSATION IT WAS ROOTED IN. A fork's hand, and the check that reads what a node left, are whole agents with no row anywhere: a hand keeps no journal at all, so its lines name the stand-in `unfiled`, and a check's name its own transcript. Grouped on that name they drew a row headed by an id nothing in the product can put a title on, beside a conversation row missing exactly that money. UsageLine.Root says whose the work was, so the row it belongs on is the conversation's own — which is also where the fold puts the money in that conversation's books.
Ties break the way UsageByModel's do, so the table is stable.
type Summary ¶
type Summary struct {
// File is the transcript, and what [Config.SessionFile] is set to to resume
// it.
File string
// Title is the name the session gave itself (title.go), empty for one that
// was never named — a session whose first turn never completed, or one that
// was had before the namer existed. A surface derives a name from Opening
// rather than showing an empty row, which is why that field is here.
Title string
// Opening is the first thing the person said, one line.
Opening string
// Last is the last thing they said — the "where was I" of the row. It falls
// back to what the agent last answered, for the session whose final message
// was a picture with no words in it.
Last string
// LastUser and LastAssistant keep the two sides of the last exchange apart
// for surfaces that show where a conversation left off. Last retains its
// picker-compatible fallback above.
LastUser string
LastAssistant string
// At is the newest timestamp in the file. Zero for a file whose lines carry
// none, which a caller fills from the file's own modification time.
At time.Time
// Asked is how many of the person's messages the FILE holds. It counts
// lines rather than turns — a compaction re-journals the messages it kept,
// so a long session counts some of them twice. A task-only conversation can
// have a saved brief and still have no chat messages.
Asked int
}
Summary is one transcript as a picker needs it.
func Peek ¶
Peek reads one transcript and reports what a picker can show of it. The boolean is false for a file that is not a conversation — missing, unreadable, a header with nothing under it, or a session with neither messages nor saved task work. Command-only conversations fall back to the saved run brief. A picker row for one of those is a row with no words on it and nothing behind it.
func Recent ¶
Recent is Peek over a directory of FLAT transcripts, newest first.
It is the old layout's reader and dies with it: a directory that may hold folder sessions beside the flat files (a machine mid-migration holds both) is read by RecentSessions, which also takes the folder's own meta.json as the name and the ordering. One function that grew a branch for each layout would be one function two waves have to agree about, so this one keeps exactly its old law.
It is bounded twice. limit is what the caller wants; peekBudget is how many files it will read to find them, so a directory holding a year of sessions costs a fixed number of scans rather than one per file. The candidates are ordered by modification time before any of them is opened — the cheap approximation of "newest" — and the answer is re-sorted by what the files themselves said, which is the fact a person recognizes.
type SummarySkipped ¶
type SummarySkipped struct{ Why string }
SummarySkipped says a pass shortened the conversation while its summary did not land. It travels as a result of /compact so the surface can report both facts even when the pass ran without an event hub.
func (*SummarySkipped) Error ¶
func (e *SummarySkipped) Error() string
type TaskAnswer ¶
TaskAnswer is the surface's reply to a proposal. Approved with an empty Redirect starts the node as briefed; a non-empty Redirect APPENDS the person's words to the brief as a correction and starts it; !Approved is a denial, and the model reads the (optional) reason as its grooming feedback. Model settles a proposal's ModelOptions, and it is read ONLY when the proposal carried some: it is the person choosing between models the harness itself could not choose between, not a surface renaming the model on work it was shown. An empty Model — and one naming anything outside the shortlist — leaves the leading option in place, which is what the card was showing.
type TaskAsk ¶
type TaskAsk struct {
Kind TaskAskKind
// Reason is the whole row sentence, complete: "conflicts with your branch:
// a.go, b.go".
Reason string
Yes string
No string
// Consequence is what saying YES will do, where that is not obvious from the
// verb alone, and it is empty on every ask whose verb says the whole of it —
// the emptiness law, applied to a sentence. Today exactly one road carries
// one: the landing held by the person's own untracked copies, where `resolve
// it` moves files of theirs ([taskAskGroundConsequence]).
Consequence string
Owner TaskAskOwner
}
TaskAsk is the question on a your-call row: the reason sentence and the two closed answers, spelled once here and read by every surface.
YES AND NO ARE THE PERSON'S OWN VERBS and not the engine's three resolutions. A surface draws them as chips and maps them back — `a` to accept or to the conflict's merge round, `n` to refute — so that "drop it" and "not right" can be the right words on their own cards without either of them becoming a fourth thing the engine has to know about.
type TaskAskKind ¶
type TaskAskKind string
TaskAskKind is which of the closed set of questions a your-call row is asking. The set is closed on purpose: a card whose reason is not one of these six is a card nobody wrote the answers for.
const ( // TaskAskStart is a proposal waiting on the person's word, with no clock to // start it. TaskAskStart TaskAskKind = "start" // TaskAskApprove is a design written and waiting to be approved. TaskAskApprove TaskAskKind = "approve" // TaskAskConflict is a branch that would not fasten onto the person's. TaskAskConflict TaskAskKind = "conflict" // TaskAskCheck is work nobody could check. TaskAskCheck TaskAskKind = "check" // TaskAskHeld is work the check did not pass, whose answer is being held. TaskAskHeld TaskAskKind = "held" // TaskAskContinue is work nothing is driving, waiting to be picked up. It is // the one ask on this table that is not about a judgement of the work: the // other five are the machine having reached the end of what it can decide, // and this one is the machine not having been there at all. TaskAskContinue TaskAskKind = "continue" // TaskAskCap is a run standing at its fuel gate. TaskAskCap TaskAskKind = "cap" )
type TaskAskOwner ¶
type TaskAskOwner string
TaskAskOwner is who holds a decision right now.
const ( // TaskAskOwnerPerson is the ordinary answer and the floor every other answer // falls back to. TaskAskOwnerPerson TaskAskOwner = "person" // TaskAskOwnerModel is `task.settle = auto`, or the person handing this one // card over ([Agent.HandUnverifiedToModel]). IT IS NEVER PERMANENT: the floor // hands it back at the end of the model's turn (agent.go). TaskAskOwnerModel TaskAskOwner = "model" )
type TaskBeatRow ¶
type TaskBeatRow struct {
// Node is the node's id, and Title what a reader would call it.
Node uint64 `json:"node"`
Title string `json:"title,omitempty"`
// Phase is one of the words above.
Phase string `json:"phase"`
// Started is when the node began, so a reader can age the whole run and not
// only the last request.
Started time.Time `json:"started"`
// Requests is how many requests this node's agents have STARTED — the
// worker's, the checker's, every repair round's and every errand's made on
// the node's behalf (task_calltrail.go), because they are all the same node
// working. A count rather than a rate: a reader comparing two
// readings gets the rate, and a rate computed here would be this file having
// an opinion.
Requests int `json:"requests"`
// RequestStarted and RequestFinished are the last request's two edges. When
// started is the later of the two, a request is in flight.
RequestStarted time.Time `json:"request_started,omitempty"`
RequestFinished time.Time `json:"request_finished,omitempty"`
// UpdatedAt is when this file was last written, which is the one fact that
// makes the rest of it believable.
UpdatedAt time.Time `json:"updated_at"`
}
TaskBeatRow is one running node's pulse as the file holds it.
func ReadTaskBeat ¶
func ReadTaskBeat(path string) (TaskBeatRow, bool)
ReadTaskBeat reads one node's pulse back. It is the whole read side, and it is here rather than in a reader's own package so that the file's shape has one definition — the same argument [runRowRecord] and [runRowNotice] are neighbours for.
A missing file is the ordinary answer for a node that is not running, and it is a false rather than an error: a reader asking whether work is alive is not asking a question that can fail.
func (TaskBeatRow) Working ¶
func (r TaskBeatRow) Working() bool
working reports whether a request is in flight: the last start is not answered by a finish.
type TaskCall ¶
type TaskCall struct {
// Model is what was asked, and Served the machine answering it — "" until
// the stream names one, which a surface draws as nothing.
Model string
Served string
// Started is when the request went out, and FirstToken when anything —
// thought or answer — first came back. FirstToken is zero until it does,
// which is the moment a person is waiting through.
Started time.Time
FirstToken time.Time
// Tokens is the answer as it arrives and Reasoning the thought underneath
// it. Both are running totals that only climb.
Tokens int
Reasoning int
// Phase is where the request is now, in internal/provider's own words for
// it: out with nothing back, parked on a provider's pacing, thinking, or
// writing. It is never [provider.CallEnded] here.
Phase provider.CallPhase
}
TaskCall is one request a node is waiting on, as far as it has got: the model asked and the machine answering, when it went out and when anything first came back, and how much has come back since.
IT IS THE SURFACE'S HALF OF internal/provider's provider.CallProgress, and deliberately less of it. How a call ENDED is not here, because an ended call is not drawn — the ladder's own sentence in TaskPhaseNotice.Text says what became of it — and the error that ended it would not survive the wire a hosted window reads this on (internal/remote's EventWire carries every field of an event as JSON). Which concurrent arm of the question this is is not here either: the notice carries the one arm nearest an answer ([callTrail.leading]), because that is the one a person is waiting on.
THE COUNTS ARE THE STREAM'S OWN RUNNING ESTIMATE and never the bill, for the provider's reason: the usage receipt is what money is counted from, and it is on the node's journal as the call's own line once the call is over.
type TaskChangeDisposition ¶
type TaskChangeDisposition string
TaskChangeDisposition is what happened to the node's edits in source control, and nothing else. It is not a claim about whether the person received a result: a research or writing task can be answered in full with no branch at all, and a kept branch is not evidence either way.
const ( // TaskChangesNone is no edits to place: no branch was made, or the engine said // nothing about one. TaskChangesNone TaskChangeDisposition = "" // TaskChangesMerged is the branch home on the ground's own branch. TaskChangesMerged TaskChangeDisposition = "merged" // TaskChangesInPlace is work done in the ground itself, with nowhere to land. TaskChangesInPlace TaskChangeDisposition = "in-place" // TaskChangesKept is a branch left standing. The engine also writes "aborted" // for this, which is the same fact in a scarier word. TaskChangesKept TaskChangeDisposition = "kept" // TaskChangesConflicted is a branch that would not fasten. TaskChangesConflicted TaskChangeDisposition = "conflicted" )
type TaskCopyRecord ¶ added in v0.4.0
type TaskCopyRecord struct {
// Dir is the directory the workers typed in and Branch is the branch their
// work is on. Dir alone is not enough: a directory whose branch is unknown
// can be read but not landed.
Dir string `json:"dir,omitempty"`
Branch string `json:"branch,omitempty"`
// Root is the repository the branch merges back into.
Root string `json:"root,omitempty"`
// Ground is the repository or folder the work is ABOUT and Mode is how the
// copy stands on it. For a worktree they repeat what Root and Branch say;
// for every other mode they are the only record of it, which is the same
// reason a node's own record carries them (task_store.go).
Ground string `json:"ground,omitempty"`
Mode TaskMode `json:"mode,omitempty"`
// Home is the root checkout's branch when this copy was cut and HomeSha is
// the commit that name held. A landing compares them with what is there now,
// so that work never follows a person who moved their checkout while the run
// was going.
Home string `json:"home,omitempty"`
HomeSha string `json:"homeSha,omitempty"`
// CheckBase is the commit at the run's start. Old records have none and
// their worker commits are left as written when the run resumes.
CheckBase string `json:"checkBase,omitempty"`
// Rung is which rung of the ground ladder made this world and Seal is the
// one string that names it, carried for the reason a node carries them: a
// landing outlives the run that made the world.
Rung GroundRung `json:"rung,omitempty"`
Seal string `json:"seal,omitempty"`
// Continues says a program's run carries on on the branch an earlier run
// of it left ([ProgramFolder.Continues]), and From is the branch its own
// was cut from when that was an earlier run's ([ProgramFolder.From]); its
// receipt says both.
Continues bool `json:"continues,omitempty"`
From string `json:"from,omitempty"`
// Snapshot is the commit a program's branch begins with that carries the
// person's uncommitted changes into its copy ([ProgramFolder.Snapshot]).
Snapshot string `json:"snapshot,omitempty"`
// Untracked files are local inputs, never part of the program's branch.
Untracked []string `json:"untracked,omitempty"`
}
TaskCopyRecord is where a run's work happened, in the fields a later process needs to find it again rather than make it again.
IT IS ADDITIVE AND ITS ABSENCE IS ORDINARY. Every run row written before this existed carries none of it, and that is not a bug to repair — it is a run whose branch genuinely was never recorded and genuinely cannot be resumed. [runCopyTree] says so out loud rather than guessing ([errNoRunCopy]).
type TaskCrewRecord ¶
type TaskCrewRecord struct {
Version int `json:"version"`
Routed bool `json:"routed,omitempty"`
Work, Plan, Check, OneModel string
Call, Title, Brief, Repo string
Decision crewroute.Decision
Original map[crewroute.Seat]string
Ladders map[crewroute.Seat][]crewroute.Pick
Rescue map[crewroute.Seat][]crewroute.Pick
Started, Bad, Broke, Gone map[string]bool
Swaps map[string]string
FreeTried map[crewroute.Seat]int
Failed map[string]crewFailureRecord
HelperUSD, DayAtStart float64
Guard *crewGuardRecord
InFlight int
}
TaskCrewRecord is the accepted routing policy and its accumulated call state. Version distinguishes an explicitly unrouted run from an old, incomplete row. An interrupted call cannot be priced reliably, so InFlight refuses recovery.
type TaskEnding ¶
type TaskEnding string
TaskEnding is WHY a node that settled `failed` stopped where it did — the one word under the state that tells a person whether to look for a fault, wait, or steer. A `failed` node carries exactly one, or none; the report's first line says the same thing in a sentence.
THEY ARE THREE KINDS OF NEWS, and a surface draws them as three. A person stopped it: nothing is wrong. The connection, a threshold, a loop, another task's working copy or a rule the worker would not follow ended it: nothing is known to be wrong, and the work can go on from its branch. The check did not accept it, or it broke: something is wrong, and the report says what.
const ( // TaskEndingStopped says a person ended it ([Agent.Cancel]). TaskEndingStopped TaskEnding = "stopped" // TaskEndingInterrupted says MACHINERY ended it — the session closing under the // node, the engine going away — without anybody marking it stopped. It is the // other half of [TaskEndingStopped]'s distinction, and it exists because the // two used to be told apart only by one of them saying nothing at all: a node a // person stopped reads `stopped` and settles failed, and a node machinery cut // stays RUNNING and resumable ([Agent.settleUnfinished]'s paused road) — so a // reader with no word for the second could not tell an interruption they had // caused from machinery that cut the work. It is NOT a finding about the work, // and [taskEndingIsFault] answers so. TaskEndingInterrupted TaskEnding = "interrupted" // TaskEndingWire says the run ended on the connection to the model rather // than on the work — a stream that reset, a socket that closed — after the // retries and the second worker (task_run.go) were spent too. TaskEndingWire TaskEnding = "wire" // TaskEndingCircling says the worker's own loop guard ended its turn // (looped.go's loopLeftUndoneNote): it kept making the same calls. TaskEndingCircling TaskEnding = "circling" // TaskEndingBlocked is [TaskEndingCircling] with a known cause: the calls it // kept making were writes into a working copy another task holds, and every // one was refused (treehold.go). TaskEndingBlocked TaskEnding = "blocked" // TaskEndingSteps says a step, progress or time threshold ended the run and // the work did not hold when it was checked ([Agent.landStopped]). TaskEndingSteps TaskEnding = "steps" // TaskEndingRefused says the run finished and the check did not accept what // it made — gaps were named, or a person refuted it. TaskEndingRefused TaskEnding = "refused" // TaskEndingStale says the work never started: what its brief assumes about // its world did not hold when the world was made, and the report names every // assumption that failed (handoffcontract.go). It is its own ending rather // than an error because nothing broke — a brief and a folder disagreed — and // because it is the one ending a person can fix by re-grounding, // re-dividing or re-briefing rather than by reading a stack of steps. TaskEndingStale TaskEnding = "stale" // TaskEndingNotes says the worker was stopped by the write-your-notes rule: // it was asked twice to write down what it was doing, its tool calls were // held until it did, and it sent three more replies with nothing visible in // them (processrule.go). It is its own ending rather than [TaskEndingCircling] // because the worker was not repeating itself — it was working in silence, // and what it worked out never reached the record its room, its check and its // parent all read. The work it did do is on its branch like any other halted // node's. TaskEndingNotes TaskEnding = "notes" // TaskEndingUpstream says the provider refused the request or would not // serve it — an API error, a refusal, a model that is not there. Like // [TaskEndingWire] it is a fact about WHO WAS ASKED and never about the work: // nothing was found out about the job, so a node that ended this way is not // evidence that anything is left to do. It is told apart from // [TaskEndingError] by [terminalProviderFailure], which reads the error's own // type, and it exists because the two used to be one word — a sibling that // died on an API 404 read as a gap in the ask and held an unattended run open // over a tree that was finished (#513). TaskEndingUpstream TaskEnding = "upstream" // TaskEndingTimeLimit says the bound a run was handed on its own time ended // it: the elapsed limit a person set on the session, of which a run is given // what is left. It is a fact about the bound and never about the work, so it // is drawn without a fault and its reason names the limit // ([taskReasonTimeLimit]). TaskEndingTimeLimit TaskEnding = "time-limit" // TaskEndingCostLimit is [TaskEndingTimeLimit] for the run's other bound: // the spend ceiling a person set, counted while the work is still going. // Its reason names the dollar limit ([taskReasonCostLimit]), and the two // endings exist apart so a person who set both is told which one fired. TaskEndingCostLimit TaskEnding = "cost-limit" // TaskEndingProgram says the program a task was handed to // (delegate_door.go) ended it without finishing, and said why: its own // check did not pass what it made, or it stopped on its own ceiling. The // program's sentence is the reason ([TaskReasonOf]), and it is not a fault: // nothing broke, a program judged its own work and said so, and what it // made is on its branch. A program that crashed is [TaskEndingError]. TaskEndingProgram TaskEnding = "program" // TaskEndingError is everything else: a working copy that could not be // made, a worker that would not start, an error nobody classified. TaskEndingError TaskEnding = "error" )
type TaskFacts ¶
type TaskFacts struct {
// State and Ending are the engine's own words, unchanged.
State TaskState
Ending TaskEnding
// Life is which of a running node's lives it is in ([TaskPhaseWorking],
// [TaskPhaseChecking], [TaskPhaseRepairing], [TaskPhaseSizing]). It is the
// typed fact the finishing reading is taken from.
Life string
// Kind and Phase are what sort of node this is and, for kinds that name their
// own moments, which moment (TaskNotice.Doing). Phases are a kind's private
// vocabulary: exactly one is interpreted here ([HarnessPhaseAsking], which has
// no typed equivalent) and the rest are carried as prose.
Kind TaskKind
Phase string
// Gap is what a nearly-finished worker is still closing (TaskNotice.Mending)
// and Hold is why a node is not spending its time on the work
// (TaskNotice.Waiting). Both are the engine's sentences about right now.
Gap string
Hold string
// Waits names unmet prerequisites, resolved to titles by the caller: ids are
// not names.
Waits []string
// Paused says the work is held at a gate only a person can open
// (TaskNotice.Paused): an adaptive run that has spent its tank. It is not a
// [TaskFacts.Hold] — a hold clears itself and nobody need act, and this one
// clears when somebody decides — which is why it is a fact of its own.
//
// It is read of a node the graph is still holding open and of no other, so a
// flag left on a row that has since landed can never contradict its ending.
Paused bool
// Stopped says a person ended this node (TaskNotice.Stopped).
Stopped bool
// Liveness is what the caller knows about a live-looking row.
Liveness TaskLiveness
// Merge and Branch are the source-control facts (TaskNotice.Merge, .Branch).
Merge string
Branch string
// Report is the landing's own account of itself (TaskNotice.Report), and it is
// read for exactly three things: which incomplete reason a fault or a check's
// finding gets ([TaskReasonOf]), the gaps a held landing names, and whether a
// landing nobody could judge leads with the check running out of time
// ([taskCheckReason]). All three read a lead this package wrote as a constant;
// nothing else here reads prose, and a node that has not landed has none.
Report string
// Consent says the work HAS NOT BEEN AGREED TO YET: a proposal in front of
// somebody, before any of it runs. Countdown is how long the consent clock has
// left, already spelled by whoever is ticking it ("9s"), and empty means there
// is no clock — either it was never set or typing held it.
//
// THE CLOCK IS THE WHOLE DIFFERENCE BETWEEN THE TWO TIERS. A proposal that
// will start by itself needs nothing from anybody and is MOVING; one that will
// sit there until somebody answers is the person's call and says so. The
// spelling of the remaining time stays with the surface that is redrawing it
// every second, and the sentence it goes into stays here.
Consent bool
Countdown string
// Cap is what a run's fuel gate is holding at, in the person's own money
// ("$5.00"), and "" when the amount is not known. It is read only beside
// [TaskFacts.Paused]: the gate is the fact, and this is the figure the question
// is about.
Cap string
// Held says the landing TURNED THE WORK BACK (TaskNotice.ResultHeld): the
// check did not pass it, and what it produced is named rather than handed on
// (task_result.go). It is not the same news as an ordinary incomplete — there
// is an answer sitting there, and taking it anyway is one of the two things a
// person may say about it.
Held bool
// Conflicts names the files that clash, for a landing whose branch would not
// fasten (TaskNotice.Conflicts). An empty list under a conflicted merge is the
// emptiness law and not a claim that nothing clashed: git does not always say
// which files it was about, and the sentence simply stops after "conflicts
// with your branch".
Conflicts []string
// Shifted says the names in [TaskFacts.Conflicts] are there because THE GROUND
// MOVED and not because the merge was refused (TaskNotice.Shifted): the branch
// would have fastened, and the person's own changed the same files while the
// work ran. It asks the conflict's question — two versions of these files,
// which survives — with the conflict's two answers, and only the reason
// sentence differs ([taskShiftReason]).
Shifted bool
// GroundHeld says the names in [TaskFacts.Conflicts] are THE PERSON'S OWN
// UNTRACKED COPIES of the files the task wrote, sitting in the folder the
// branch merges into (TaskNotice.GroundHeld, groundcarry.go). It asks the
// conflict's question with the conflict's two answers and differs only in the
// sentence ([taskShiftReason]) — and in what saying yes DOES, which is why
// the ask carries a consequence on this road and on no other.
GroundHeld bool
// Decider is WHO HOLDS THE DECISION right now (TaskNotice.Decider). The zero
// value reads as the person, which is the only safe reading of a caller that
// said nothing: work whose owner nobody recorded is work waiting on whoever is
// looking at it.
Decider TaskAskOwner
// CannotContinue is WHY work nothing is driving cannot be picked up again,
// in the words a person reads, and empty when it can be. It is the sentence
// and not a flag, because the row has to say it and a flag would make some
// surface write those words a second time ([runCannotContinue] holds the
// one spelling).
//
// IT IS ONLY EVER SET ON AN INTERRUPTED ROW. Every other state is either
// over or moving, and neither has anything to carry on.
CannotContinue string
}
TaskFacts is everything the reading uses. A caller fills in what it has, and every absent field stays an absence.
type TaskGraph ¶
type TaskGraph struct {
// contains filtered or unexported fields
}
TaskGraph is the session's work as a directed acyclic graph, plus the frontier executor that runs it.
type TaskIndexEntry ¶
type TaskIndexEntry struct {
// ID is the node's id inside the session that ran it, decimal. It is NOT
// unique across the file — ids restart with every conversation — which is
// why SessionID sits beside it and why the pair is what identifies a row.
// It is a string because it is a handle a person types and a model quotes,
// not a number anything does arithmetic on.
ID string `json:"id"`
// Parent is the id of the family root inside this session, or empty for a
// root. It is additive: rows written before families entered the index
// decode as roots, which is exactly what they were.
Parent string `json:"parent,omitempty"`
// Name is the slug an "@" mention resolves: the title, kebab-cased
// ([TaskSlug]). Two tasks may share one — a project that fixed the same
// crash twice — and the newest wins, because "the nil-map task" said out
// loud means the last one.
Name string `json:"name"`
// Label is the title as a ROW shows it: cut to [taskLabelLimit] once, here,
// so that every surface drawing this index draws the same words. Title is
// the title as it was groomed, uncut, for the pointer block and the search.
Label string `json:"label"`
Title string `json:"title"`
// Kind is what SORT of work this row was: an adaptive run, a saved shape
// running, a saved shape being made — or empty for ordinary work, which is
// most of the file ([TaskKind], and [TaskKindWord] for the word a person
// reads).
//
// IT IS ADDITIVE AND ABSENCE IS ORDINARY. Rows written before this field
// existed decode with none, which is exactly what almost all of them were;
// unlike [TaskIndexEntry.Files], where absence is unknown, there is nothing
// here for a reader to be careful about — a blank kind and a plain task are
// drawn the same way on purpose.
//
// IT IS HERE BECAUSE THE KNOWLEDGE WAS BEING THROWN AWAY ON THE WAY TO THE
// FILE. A node has carried its kind since it was admitted ([TaskNode.kind],
// from [taskSpec.kind]) and an adaptive run has always known it was one, and
// a surface that wanted to say "adaptive" on a row could only string-match
// the title or sniff the shape of the transcript's path — both of which are
// guesses about a fact the engine held.
//
// A BACKGROUND JOB NEVER REACHES THIS FILE, and the constant existing does
// not change that: jobrow.go's own law is that a job publishes a roster row
// and no project index row, no landing note and no card. [TaskKindJob] is
// spelled in [TaskKindWord] so that a live row merged in from a graph reads
// the same way as a landed one, not because the file holds any.
Kind TaskKind `json:"kind,omitempty"`
// Program is the program codeaf carries that this work was handed to —
// senior-dev — and empty for every task a conversation's own worker did
// ([TaskNotice.Program]). It is what lets a surface drawing this file — the
// `@` list, home, the tasks place, another conversation's tasks tool — tell
// a program's work from an ordinary task's, which it otherwise could not
// do from anything a row carries.
//
// IT IS ADDITIVE AND ABSENCE IS ORDINARY, on [TaskIndexEntry.Kind]'s terms:
// rows written before the field existed decode with none, and a blank program
// and a plain task are drawn the same way on purpose.
Program string `json:"program,omitempty"`
// Where is the worker's resolved directory, or the explicit placement from a
// restored proposal that has not started yet.
Where string `json:"where,omitempty"`
// Ground is the repository or folder the work WAS ABOUT, absolute, and Mode
// is how it stood on it ([TaskMode]). Where names a task folder under a
// session, which tells a person where the machinery was; these tell them
// where their work went, which is the question a row in a project's own
// history is asked (taskstands.go).
//
// THEY ARE ADDITIVE AND ABSENCE IS UNKNOWN, like Files beside them: a row
// written before they existed says nothing about its ground, and a reader
// draws nothing rather than assuming the session's own folder.
Ground string `json:"ground,omitempty"`
Mode TaskMode `json:"groundMode,omitempty"`
// Rung is which copy of the ground the work happened in — the rung of the
// ground ladder that made this node's world (groundladder.go). Mode above is
// the PROMISE, settled before anything was carved; this is what was actually
// made to keep it, and the two are not the same fact: one repository task
// gets a worktree and the next a whole fork, and both were promised a branch.
//
// IT IS HERE BECAUSE A CARD CANNOT GUESS IT. The settled card names the
// directory the work was left in, and until this field existed the only word
// it had for that directory was the one the surface had hardcoded — which was
// right for one rung and wrong for the rest ([GroundWord] holds the words).
//
// ADDITIVE, AND ABSENCE IS UNKNOWN, like Files and Ground beside it: a row
// written before it existed says nothing about its rung, and the promise is
// what a reader falls back to rather than assuming the rung that was ordinary
// on the day the row was written.
Rung GroundRung `json:"groundRung,omitempty"`
// Status is the node's final state — "done", "failed", "unverified" — or its
// live one ("running", "queued") on a row merged in from a graph that is
// still turning.
Status string `json:"status"`
// Ending is why a failed node stopped ([TaskEnding]). It is the existing
// engine fact a surface needs to distinguish work a check refused from work
// that broke, without guessing from Outcome's prose.
//
// IT IS ADDITIVE AND ABSENCE IS UNKNOWN. Rows written before this field
// existed keep their old failed presentation; only a row that explicitly
// carries TaskEndingRefused may be presented as incomplete.
Ending TaskEnding `json:"ending,omitempty"`
// Outcome is the first sentence of the node's report: what it did, or what
// stopped it. Empty for work that has not landed.
Outcome string `json:"outcome"`
// FilesChanged is how many files the node wrote, and it is the figure every
// surface draws. It is also the HONEST TOTAL: Files beside it is capped at
// [taskFilesLimit], so a node that wrote more has a count larger than its
// list, and the count is what says so.
FilesChanged int `json:"filesChanged"`
// Files are those same paths, repo-relative and slash-spelled, in the order
// the node first wrote them, capped at [taskFilesLimit].
//
// THE COUNT IS FOR READING AND THE LIST IS FOR ASKING. This row used to carry
// the count alone, on the argument that the list was in the transcript — and
// it is, but only as prose in a journal, which is no use to the question the
// list is here for: "did anybody else land work in these files, and when".
// Answering that off the transcripts would mean opening every one of them.
//
// BOTH ARE WRITTEN FROM ONE PLACE ([taskFileCitations]) so the count and the
// list can never drift apart.
//
// IT IS ADDITIVE, AND ABSENCE IS UNKNOWN. Rows written before this field
// existed decode with none, and none does NOT mean the node touched nothing:
// a reader that cannot see a list must say it cannot see one rather than
// invent an answer (the emptiness law), which is why [LandedTouching] answers
// with two lists instead of one.
Files []string `json:"files,omitempty"`
// MaySplit is WHETHER THIS WORK WAS EVER ALLOWED TO HAND ITS PARTS OUT, and
// which reader allowed it: "wide" for a model's own judgement of breadth,
// "judged" for the sizing call at the typed door, "counted" for a brief that
// named enough separate items on its own (task_divide.go's arming words).
// ABSENT MEANS THE VERB WAS NEVER ON THE BELT.
//
// IT IS HERE BECAUSE THE ABSENCE OF PARTS IS THREE DIFFERENT FACTS. A row for
// a task that ran alone can mean the worker was never given `divide_work`,
// or had it and never reached for it, or asked and was told no — and until
// this field existed the file said the same thing about all three, so anybody
// reading the record to find out whether the road was working could only
// count parts and guess. The reason word separates the first from the other
// two, and separates a road nobody armed from a road nobody used.
//
// IT IS THE READING AND NOT THE OUTCOME. A task armed and never divided still
// says so, because what this answers is what the task was ALLOWED to do; the
// parts themselves are rows of their own, carrying this node's id as Parent.
MaySplit string `json:"maySplit,omitempty"`
// Cost is what the node spent, in dollars, or 0 when nobody could say.
Cost float64 `json:"cost,omitempty"`
// Model is what the node ran on, and empty when it simply took the
// conversation's. It is here because the file that carries the COST is the
// file that has to be able to answer "at what rate": the id lives on the
// checkpoint and in the node journal's header too, and a row without it made
// re-pricing a landed task a three-file join.
Model string `json:"model,omitempty"`
// RepairedOn is the model a REPAIR ROUND ran on, and it is here only when
// that was not the model beside it: work the checker sent back is handed to
// the careful tier (internal/session's repair_role.go), and this is the one
// row in this file that says an escalation was bought.
//
// IT IS THE COMPANION TO Cost AND IT ANSWERS THE SAME QUESTION Model DOES,
// one layer down. A node's bill is the sum of every agent it took — the
// worker, each correction round, each check — so a row carrying one model and
// one figure cannot say whether an expensive total was an expensive task or a
// cheap task that needed rescuing, and those are different facts about a
// crew's economics. Additive, and absent means the ladder floored: either
// nothing was sent back, or the careful tier resolves to the model the work
// was already on.
RepairedOn string `json:"repairedOn,omitempty"`
// Tokens is input plus output, as ONE sum. The index carries citations, and
// the four-way split — with the cache share in it — lives in the journal
// this row's TranscriptURI names; a row that spelled out all four would be
// the thing this index refuses to be. Zero means nobody counted, never that
// the work was free.
Tokens int `json:"tokens,omitempty"`
// DurationMS is how long it ran.
DurationMS int64 `json:"durationMs,omitempty"`
// EndedAt is when it landed. It is zero for a row merged in live and for a
// row rebuilt from a record that never carried the landing instant. Every
// ordering in this file is on it (see [taskIndexAt]).
EndedAt time.Time `json:"endedAt"`
// VerdictBasis is HOW this row's verdict was earned, read from the project
// record ([plandb.SetVerdictBasis]) and carried here so the tasks view never
// has to reopen a trajectory to say whether a verdict was a read or a run.
// It is a surface projection and is never painted: the state and outcome a
// person sees stay exactly as they were (taskview_test's
// TestTheTasksViewReadsThePersistedVerdictBasisWithoutNewWords).
VerdictBasis *plandb.VerdictBasis `json:"verdictBasis,omitempty"`
// StartedAt is when the node began running — the node's own start, which
// the checkpoint restores with it (task_store.go's taskRecord), so a row
// rebuilt tomorrow carries the real instant rather than the moment a window
// happened to meet it. It is zero for a queued node and for every row
// written by a build older than this field, and a surface draws no age for
// either (the emptiness law).
//
// THE SPELLING IS THE INDEX'S OWN AND THE CHECKPOINT'S: camelCase, as
// `endedAt` beside it, and omitzero because a time is a struct and omitempty
// never leaves one out.
StartedAt time.Time `json:"startedAt,omitzero"`
// SessionID is the conversation that ran it — the id in the journal's
// header, which is also the directory a node's own transcript sits under.
SessionID string `json:"sessionId"`
// Branch is the node's own task branch when its work was KEPT there — a node
// that settled without merging home (a failure, a stop, a check nobody could
// pass) or whose landing was deliberately left on a protected, moved or
// detached checkout. It is the branch string VERBATIM, and it is empty for
// work that came home or was laid in place.
//
// IT IS THE SAME FACT [TaskIndexEntry.ArtifactURI] HAS ALWAYS CARRIED INSIDE
// ITS `git:` SPELLING, given a field of its own so a reader can name the
// branch without parsing a URI — and so the chat side says the same three
// words #1182 put on the headless envelope (`kept_branch` and its `verdict`,
// read off this row's [TaskIndexEntry.Status]).
//
// IT IS ADDITIVE AND ABSENCE IS UNKNOWN, like Files and Ground beside it: a row
// written before it existed says nothing about its branch, and a reader draws
// nothing rather than assuming one was kept.
Branch string `json:"branch,omitempty"`
// ArtifactURI is where the WORK is: the node's worktree while one is on
// disk, else the branch it was kept on, else empty for a node whose changes
// went straight into the person's tree.
ArtifactURI string `json:"artifactUri,omitempty"`
// TranscriptURI is where the STORY is: the node's own session journal, which
// is a real session file the read tool can open (task_run.go's
// taskJournalPath).
TranscriptURI string `json:"transcriptUri,omitempty"`
// Activity is what a RUNNING node is doing at the instant this row was
// built, in one line: the call in flight and how long it has been in flight,
// or the gap between calls with the step count beside it (task_live.go). It
// is empty on every landed row.
//
// IT IS NEVER WRITTEN TO THE FILE. The index is what work CAME TO, and a row
// on disk claiming a call in flight would be this project's record
// remembering a present that ended seconds after it was recorded — which is
// the one thing an append-only history must not do.
Activity string `json:"-"`
// Phase is which of a RUNNING node's three lives the row was built in, in
// the words task_contract.go exports ([TaskPhaseChecking] and the other
// two): its own worker, the check that reads what the worker left, a repair
// round closing what the check found. It is empty on every landed row, and
// empty on a running one this process does not hold the graph for.
//
// IT IS NEVER WRITTEN TO THE FILE, for [TaskIndexEntry.Activity]'s reason
// said once: the index is what work CAME TO, and a phase is what it is doing
// this second.
Phase string `json:"-"`
}
TaskIndexEntry is one node as the project remembers it.
It is a SEPARATE type from TaskNotice and from taskRecord, and deliberately: a notice is what is happening now, a record is a node's resumable state, and this is a citation — the smallest thing that lets a person or a model find the work again. The fields that are here and nowhere else are the two URIs and the cost, because those are the three questions asked about work that is already over.
func LandedTouching ¶
func LandedTouching(rows []TaskIndexEntry, files []string, after time.Time) (touching, unknown []TaskIndexEntry)
LandedTouching is the work that FINISHED in one of these files since a moment — and, separately, the work that finished since then without saying which files it wrote.
It is the ground-shift question: a node started against a file an hour ago, somebody else's task has landed in that file since, and the node is now working from a copy of the world that no longer exists. The answer is a citation, which is what TaskIndexEntry is for — the caller is expected to take the row's TranscriptURI or ArtifactURI and go and look.
rows are the index as ReadTaskIndex returned them and the order is carried through untouched: newest first, which is the order somebody reading "what has happened since" wants.
LIVE ROWS ARE NOT LANDED WORK and are left out of both lists. A row saying running is a claim about a process, and the file is the wrong place to ask about a process — Elsewhere.Touching is the right one, and a row appearing in both answers would be one piece of work counted twice.
A ZERO after MEANS THE WHOLE FILE, which is what a caller with no starting moment actually wants; the row's EndedAt has to be strictly after it, so a caller passing the moment it last looked is not handed back the row it looked at.
func LookupTask ¶
func LookupTask(rows []TaskIndexEntry, token string) (TaskIndexEntry, bool)
LookupTask resolves one "@" token — a slug, or an id — against the index.
The NEWEST match wins. Slugs are derived from titles and titles repeat: a project that fixed the same crash in March and again in August has two rows called fix-the-nil-map-crash, and "the nil-map task" said out loud in September means the August one.
func ReadTaskIndex ¶
func ReadTaskIndex(path string) []TaskIndexEntry
ReadTaskIndex reads the rows at path, NEWEST FIRST, and tolerates everything.
A missing file is the ordinary case — a project that has never run a task — and answers nil. A line that does not parse, or that parses into a row with no title, is skipped: see this file's header for why that is a rule and not a defect.
func SearchTaskIndex ¶
func SearchTaskIndex(rows []TaskIndexEntry, query string, limit int) []TaskIndexEntry
SearchTaskIndex ranks rows against a query and returns at most limit of them.
An EMPTY QUERY is not an error and not everything: it is "the most recent work", which is what both callers want when the person has typed "@" and nothing after it, or when the model asked what has been going on.
The needle is matched against the TITLE, the ID and the OUTCOME, in that order of worth. Those three are what a person half-remembers: what it was called, which number it was, and what it turned out to be. The brief and the files are deliberately not searched — a query that matched every task that ever touched session.go would be a search that answers "all of them".
func (TaskIndexEntry) Duration ¶
func (e TaskIndexEntry) Duration() time.Duration
Duration is how long the work ran: DurationMS for a row that has landed, and the time since TaskIndexEntry.StartedAt for a LIVE row that knows its start.
A LIVE ROW'S DurationMS IS A STALE CLOCK. It is the node's age at the instant the row was built, so a row held in a cache for a minute says the work had run a minute less than it has; the start does not go stale. A row that is not live keeps its frozen figure even with no landing instant on it — a record rebuilt from an older build — because counting up from a start there would measure a run that ended long ago as though it were still going.
func (TaskIndexEntry) Live ¶
func (e TaskIndexEntry) Live() bool
Live reports whether this row is a node that is still going.
func (TaskIndexEntry) StatusFacts ¶
func (e TaskIndexEntry) StatusFacts(held bool) TaskFacts
StatusFacts is one record row as the reading takes it. `held` is the caller's authoritative liveness (SessionRow.Runs); false becomes UNCLAIMED only because that method's ladder — a live conversation's claim list, then the lock — is a negative answer rather than the absence of one.
A record row knows less than a live one. The index carries no merge word, hold, gap or prerequisite, and it carries only the KEPT branch (TaskIndexEntry.Branch) rather than the merge that left the work there — so a row read from it can say what state it is in, why it ended, and where its edits were kept, and never what is holding it.
type TaskKind ¶
type TaskKind string
TaskKind is WHAT SORT of work a node is, and the empty string is the ordinary one this whole file is written about: a piece of work handed to a child agent in a worktree of its own.
IT IS NOT A STATE AND IT NEVER CHANGES. A node is admitted as one kind and settles as that kind; what moves is TaskState underneath it. The reason a surface needs it at all is that the two kinds are honestly different objects to draw — one has a branch, files it changed and a merge, and the other has none of those and could never have them — so a card that promised "the branch it wrote on is kept" over a design would be pointing at work that does not exist.
const TaskKindAdaptive TaskKind = "adaptive"
TaskKindAdaptive is a run that PLANS ITSELF as it goes (orchestrate.go): a family whose root is a goal and whose children crystallize while the run is turning, so the denominator a row could quote is "planned so far" and not a number anybody fixed at the start.
IT IS NOT ONE OF [taskSpec.kind]'S ANSWERS, and it cannot be: an adaptive run has no TaskNode and no spec at all — its three index rows are written straight from the family seam ([orchestrateFamily]). It is a TaskKind rather than a fourth vocabulary because the one question every surface asks of a landed row is "what sort of work was this", and an answer that lived in two enums would be two answers.
const TaskKindHarness TaskKind = "harness"
TaskKindHarness is a subharness being designed (harness_task.go): no worktree, no branch, no files, and a page that reaches the registry only if the person approves the card at the end of it.
const TaskKindJob TaskKind = "job"
TaskKindJob is a piece of BACKGROUND WORK this session started that is not an agent at all (jobrow.go): a command running under `bash background:true`, a foreground command that reached its bound and was promoted (promote.go), a watch, a video render. It has a log file and an exit code, and no worktree, no branch, no room and no report anybody wrote.
IT IS ON THE ROSTER BECAUSE OF WHERE WORK SHOWS, NOT BECAUSE IT IS A TASK. The law is that work this conversation started shows on the right, whatever door started it — and a background job was, until this kind existed, the one kind of work with no row anywhere: the registry knew about it, the `jobs` tool could list it, and the column beside the conversation stayed empty while a nine-minute sweep ran. A person watching that column had no way to tell a session that was working from one that had quietly stopped.
WHAT THAT COSTS, said plainly, is the same price an adaptive run's rows pay (orchestrate.go's family seam): the id on the row names nothing in the task graph, so a surface that offers to open a node's room or stop it by id will find no node there. A job is read with the `jobs` tool and stopped with `jobs kill`, and its row is a row.
const TaskKindQuick TaskKind = "quick"
TaskKindQuick is a task that runs WHERE ITS CALLER WORKS (task_quick.go): the caller's own workspace, no worktree, no branch, no merge, no check and no landing card. Its last message is its result, and the row goes `done`.
IT IS THE KIND WITH NOTHING UNDER IT, and that is what a surface has to know about it. Every other kind of node has something a card can point at — a branch coming home, a page waiting on a yes, a program's typed output — and this one has an answer and, when it wrote, the files it wrote. A card that promised "the branch it wrote on is kept" over a quick node would be pointing at work that does not exist, which is exactly what TaskKind exists to prevent.
const TaskKindSubharness TaskKind = "subharness"
TaskKindSubharness is a subharness RUNNING (subharness_run.go): a saved program taking one piece of typed work, with a room, a journal of its host calls, and a ✕ — and, like a design, no worktree, no branch and no merge.
IT IS THE ASYMMETRY THIS WAVE CAME TO FIX. Designing a harness has been a task since harness_task.go landed; RUNNING one blocked the conversation as a turn (harness.go), which made the one thing a person most wants to walk away from the one thing they could not. A run is a node now, and everything a node has — the row, the room, the id, the stop — it has for free.
type TaskLanding ¶ added in v0.3.0
type TaskLanding struct {
ID uint64
State TaskState
Brief string
Deliverable string
Report string
Claim string
Ending string
Wrote []string
Changed int
Checks []string
Worker string
High string
CostUSD float64
Tokens int
// Attempt is which run of this node the landing is, so a per-run judged
// marker survives a resettle (same id, re-judged) and a re-run (new attempt).
Attempt int
}
Config builds one agent. The zero value is invalid: Workspace, Model and BaseURL are required. APIKey may be empty for a session opened before the person has handed one over — the first-run setup's case — and every request refuses until Agent.SetAPIKey lands it. TaskLanding is one landed node, handed whole to Config.TaskLanded. It is the record a caller outside this package needs to judge the work: what was asked, what came home, who ran it and who checked it, and what the run spent. The fields are copied from the node's own record at the moment the node reached a final state, so a reader that arrives late reads a fact rather than a half-open run.
func LoadLandedForJudge ¶ added in v0.3.0
func LoadLandedForJudge(tasksPath string) ([]TaskLanding, error)
LoadLandedForJudge reads a session's persisted task checkpoint at tasksPath and returns one TaskLanding per node in a final state (Done, Failed or Unverified), rebuilt from the record so a reader that arrives after the process that ran the node is gone can still judge it. It mirrors (*TaskNode).landing() with two differences forced by reading off disk: High is the persisted CheckedOn (empty on older records), and Tokens is the record's own Input+Output, because the live room's usage a landing also counts is gone with the process. A missing or unreadable checkpoint yields no landings and no error, the same nothing recoverTasks reads it as.
type TaskLanes ¶
type TaskLanes struct {
// contains filtered or unexported fields
}
TaskLanes is THE COUNT OF RUNNING LANES ON ONE MACHINE, and the one divisor the governor's reading is shared over. Every task graph that belongs to the same running codeaf writes its own starts and hand-backs into one of these, so the count beside a reading of the whole process tree is drawn from the same population the reading is (#907).
WHICH GRAPHS SHARE ONE IS SAID, NOT ASSUMED. The process's own door builds one and hands it to every conversation it opens (Config.TaskLanes); a graph given none is alone in its process and keeps one of its own ([newTaskGraph]). A package-level variable would have been the same claim made silently, and it is a claim no library can make for its caller — a test binary is one process and a hundred unrelated machines.
It is one mutex and one int, and that is the whole design. IT IS THE INNERMOST LOCK here: every caller either holds a graph's lock already or holds nothing, and nothing inside it calls back out, so it cannot be half of a cycle. It is nil-safe throughout, because a graph assembled field by field in a test has no account and a count of nothing is the truth about it.
func NewTaskLanes ¶
func NewTaskLanes() *TaskLanes
NewTaskLanes builds the account one machine's conversations share. The door that opens conversations builds one for the life of the process and puts it on every Config it hands out.
type TaskLiveness ¶
type TaskLiveness string
TaskLiveness is what the caller knows about whether anything is behind a row that claims to be running or queued. Unknown is the default and is preserved: a node absent from one process is not a dead node, and work outlives the window that started it.
const ( // TaskLivenessUnknown is no authoritative answer; the reading follows the // state as claimed. TaskLivenessUnknown TaskLiveness = "" // TaskLivenessHeld is a positive claim: something holds this node. TaskLivenessHeld TaskLiveness = "held" // TaskLivenessUnclaimed is an authoritative negative — no live claim exists // anywhere the caller can see, which is the judgement [SessionRow.Runs] makes // from the presence file and the lock. It is not "no terminal is attached". TaskLivenessUnclaimed TaskLiveness = "unclaimed" )
type TaskMode ¶
type TaskMode string
TaskMode is HOW a task stands on its ground — the one word that says whether the work is isolated from the place it is about, and how it comes back to it.
IT IS DERIVED FROM THE DELIVERABLE AND NEVER ASKED. Nobody is made to answer a question about worktrees to get a piece of work started: the ground is resolved from what the conversation already holds (taskstands.go's ladder) and the mode falls out of what the work has to leave behind — writing in a repository is a branch, reading one is not, and a folder that has no history to branch from is copied or worked in directly.
IT IS A RECORD, NOT A SCREEN WORD. A surface says "in ~/x · branch off main", which is these facts spelled as a sentence; none of these five strings is meant to be drawn as it stands.
const ( // TaskModeWorktree is a branch cut FROM THE GROUND off its HEAD, worked in a // directory of the task's own and merged back when the destination is an // ordinary branch. A protected, moved or detached checkout leaves the task // branch kept instead. It is what ordinary work in a repository gets, and // the person's uncommitted changes are not carried into it — the branch comes // off HEAD and nothing else. TaskModeWorktree TaskMode = "worktree" // TaskModeReference is work that only READS its ground: the task gets a // folder of its own to write in and the ground stays read-only for it. TaskModeReference TaskMode = "reference" // TaskModeMirror is a plain folder — no history to branch from — copied into // the task's own directory, worked in there, and landed back by name. TaskModeMirror TaskMode = "mirror" // TaskModeInPlace is the person saying "here" about a referred place: the // work happens in the ground itself, with nothing isolating it and the turn's // file ledger (recovery.go) as the only undo there is. A model cannot choose // this mode inside a repository; its `where` is redirected to a branch. TaskModeInPlace TaskMode = "in place" // TaskModeFolder is the honest nothing — the conversation's own folder, with // no repository anywhere under it. The work happens there because there is // nowhere else it could be about. TaskModeFolder TaskMode = "folder" )
type TaskNode ¶
type TaskNode struct {
// Ground is the repository or folder THIS WORK IS ABOUT, absolute, and Mode
// is how the node stands on it ([TaskMode]). They are settled before the
// node's first tool call — at the door for work somebody proposed
// (taskstands.go), and by the working copy itself for everything else — and
// they never move afterwards.
//
// THEY ARE WHAT EVERY OTHER PART OF THE MACHINERY ASKS. The guard asks which
// directory a write may land in, the audit asks which repository a clean
// restore is cut from, a card asks which project to name, and a checkpoint
// carries them so a resumed node knows the same thing this one did. Before
// they existed each of those answered for itself out of the worktree it
// happened to hold, and issue #76 is what that cost: four parts of one
// program disagreeing about which repository an hour of work was for.
//
// They are exported among unexported neighbours because they are read from
// every one of those places by name; like the fields around them they are
// guarded by the graph's lock and written once.
Ground string
Mode TaskMode
// Home is the branch the root checkout was on when this node's branch was
// cut. It is the fixed side of the moved-checkout question at landing time;
// absence is ordinary for a checkpoint written before this field existed.
Home string
// HomeSha is the commit Home named at the cut. The branch name answers which
// destination the person chose, but only this commit answers which world that
// name held before they and the task went on working independently.
HomeSha string
// Rung is which rung of the ground ladder made this node's world and Seal is
// the one string that names that world — furrow's sealed snapshot, or the
// machine commit's sha (groundladder.go). They are written beside Ground and
// Mode by [TaskNode.setTree], from the tree that was actually made, and they
// are what lets a report say what world the work was done in.
Rung GroundRung
Seal string
// Frozen is THE WORLD THIS NODE STARTS FROM, when it is a part of a family
// that froze one: the commit its parent's division put the family tree at
// before any part of it was admitted (task_divide_wip.go). The ground ladder
// carves this node's working copy from it and seals nothing.
//
// WITHOUT IT THE SIBLINGS GET DIFFERENT WORLDS. A part's working copy is
// prepared lazily, when the frontier starts it, and a parent goes on working
// while its parts run — so two parts cut a minute apart would each inherit
// whatever the parent's directory happened to hold at that instant, and the
// division that named their boundaries would have described neither.
//
// IT IS THE CHILD'S OWN FIELD AND NOT A LOOKUP ON THE PARENT, which is the
// difference between a fact and a variable: a parent may divide more than
// once, and a second division reading a field the first one wrote would put
// the new parts in the old world — or, worse, move the old parts' world under
// them. It is written at admission, from the spec, and never again.
//
// IT IS ON THE CHECKPOINT (task_store.go) because a resumed part must not
// reseal: coming back after a restart and inheriting the parent's tree as it
// stands NOW would be the same divergence arriving through the one road that
// does not prepare its tree at the door.
Frozen string
// Family is THE CHECKS THIS NODE OWNS FOR THE WHOLE FAMILY IT HANDED OUT: a
// check that every part of a division was told to run, taken off all of them
// and given to the one node that can honestly make it — this one, once, after
// every part's work is home (task_divide_scope.go).
//
// IT IS A FIELD RATHER THAN A WRITE TO THE SPEC, and that is a law and not a
// convenience. NOTHING IN THIS PACKAGE WRITES A SPEC AFTER ADMISSION — the
// contract a node was admitted with is the contract it is judged against, and
// [TestEachPartCarriesItsOwnDoneConditionAndTheParentKeepsTheOriginal] pins
// it. So the repair does not edit `spec.acceptance`; it puts what it lifted
// HERE, where the reader that needs it can find it: the worker is TOLD what it
// owns ([TaskNode.instructionOn]) and runs it in its own hands. What its
// CHECKER may re-run is the narrower [TaskNode.FamilyDeclared], for the reason
// stated there.
//
// IT IS ON THE CHECKPOINT (task_store.go), UNLIKE [TaskNode.sharedTold], and
// the two go opposite ways for one reason: the telling is about a
// conversation that is over, and this is about a run that has not happened
// yet. The parent's own check is made after every part is home, which can be
// hours later and a different process from the one that divided — a resumed
// node that had forgotten it would be a check nobody ever makes.
//
// It is exported among unexported neighbours for [TaskNode.Ground]'s reason:
// it is read by name from more than one place. Like them it is guarded by the
// graph's lock.
Family []string
// FamilyDeclared is the part of [TaskNode.Family] this node's CHECKER may
// re-run: the family's checks that a part had DECLARED as verification rather
// than ones this package recognised in a part's prose done-condition
// ([declaredAmong]).
//
// THE TWO LISTS ARE TWO PERMISSIONS AND THAT IS WHY THERE ARE TWO. Family is
// what the parent's worker is told to run once its parts are home, which is
// ordinary work in ordinary hands. This is executable verification, and
// lifting must move it rather than mint it: a command nobody ever typed into
// `checks` was executable verification nowhere, so making it the parent's door
// would be the prose harvest this build removed, arriving by the family road.
//
// A CHECKPOINT WRITTEN BEFORE THIS FIELD CARRIES NEITHER PROVENANCE NOR THIS
// LIST, and it is read as declaring nothing. Old records were filled by a
// build that harvested commands out of prose; granting them fresh execution
// now, on the strength of a list nobody can tell apart any more, is the one
// thing this field exists to prevent.
FamilyDeclared []string
// FamilyWas is what this node was required to run for its family BEFORE the
// goal moved. A revision takes the family's checks off the node — both the
// half its checker could run and the half its worker was told to run — because
// an instruction written for the old goal standing in front of the new one is
// the contradiction a person reading the card would have to resolve. Keeping
// them here rather than dropping them means what was required is still legible
// afterwards, on the node and on the checkpoint, without being required.
FamilyWas []string
// Checks is THE REPEATABLE VERIFICATION THIS WORK WAS PUT UNDER CONTRACT WITH:
// the commands whoever proposed it declared as the way anybody re-establishes
// that it is done, typed into `checks` rather than harvested out of prose
// (task.go's schema, task_checks.go's law). They are the ONLY commands this
// node's checker may run, beside the reading ones, and an empty list — which is
// most nodes, and every node admitted before this field existed — means the
// checker judges from what it can see and from the worker's receipts.
//
// IT IS A NODE FIELD AND NOT ONLY A SPEC ONE for the reason [TaskNode.Family]
// is: a check can be moved by something that happens after admission. The spec
// carries what the proposal declared, this carries what the node is checked
// against NOW, and [TaskNode.reviseChecks] is the one door between them —
// because a steering revision that changes the goal must not leave the old
// goal's checks standing in front of the new one.
//
// It is on the checkpoint (task_store.go) for Family's reason: the check is
// made when the work comes home, which can be a different process from the one
// that admitted it. Like its neighbours it is guarded by the graph's lock.
Checks []string
// Base is the machine commit the parent's world was sealed into and Universe
// is furrow's name for the fork, when a rung made either. They are here for
// the SAME REASON Rung and Seal are — the landing needs them and the landing
// does not always happen in the run that made the world. A person who accepts
// a task hours later, or a session resumed after a crash, rebuilds the
// working copy from these four fields ([TaskNode.workingCopy],
// [TaskNode.resumeTree]); without them the inheritance would be merged back
// over the person's own edits and a branch in a fork would be looked for in
// the wrong repository.
Base string
Universe string
// CheckBase is the immutable Git commit captured before the worker runs.
// A universe's Seal names a filesystem snapshot, not a Git commit.
CheckBase string
// Expects is THE CHECKABLE HALF OF THE HANDOFF this node was given: what its
// brief assumes is already true of the world it gets (handoffcontract.go).
// It sits beside Ground for the same reason Rung does — the ground says
// which world, and this says what the world was promised to contain — and it
// is written once, at admission, from the spec.
//
// The contract is answered before the node's first step, so this is restored
// from a checkpoint only onto a node that has NOT yet run (task_store.go's
// [expectsOwed]); one that started has been through it, and handing it the
// manifest again would put a settled question to a working copy its own work
// has since changed. The spec's copy survives either way, because the brief
// carries it as a section.
Expects []Expectation
// contains filtered or unexported fields
}
TaskNode is one piece of work: what it was admitted with, where it is in its life, and what it leaves behind.
Every field below the mutex line is guarded by the GRAPH's lock, not one of its own. A node is never touched alone — starting one reads its dependencies' reports, finishing one unlocks its dependents — so a second lock would be a lock ordering to get wrong for no gain.
── THE GOAL CONTRACT: spec IS FROZEN AT ADMISSION ──
NOTHING IN THIS FILE WRITES spec.request, spec.brief, spec.deliverable OR spec.acceptance AFTER admit — the four parts of what the node is told (task_brief.go). Not the frontier, not the runner, not the auditor, not a redirect that arrives late. The node's goal is settled the moment [TaskGraph.admit] takes it, and every reader downstream — the instruction the child is given, the acceptance the auditor judges against, the report a dependent inherits — reads THAT text and no other.
This is Argus's two-tier goal contract (harness-research-notes.md §1, arXiv:2608.05144), and the two tiers are drawn exactly here: the semantic tier moves freely BEFORE admission — the model grooms the brief, the person redirects it and their words are appended (task.go) — and the precise objective moves only with authority, which in this surface means a NEW admission by the person. A redirect is not an edit to a running node; there is no path to one, and there must not be. The reason is the auditor: a frontier that verifies work against an acceptance which can move while the work runs verifies nothing, because whoever holds the pen can always make the work pass. What the person gets instead is honest — the running node lands against what it was given, and the correction is a task of its own.
The only fields below that change after admission are the node's LIFE (state, started, cancel) and its LEAVINGS (brief, report, changed, branch, merge). brief is assembled once at start (runFrontier's JIT assembly) from spec.brief plus prerequisites' reports and is not the spec.
type TaskNotice ¶
type TaskNotice struct {
// Thinking is the effective setup, including a saved continuation choice.
Thinking string
// ID is the proposal's token: a surface hands it back to
// [Agent.ResolveTask]. On updates it names the node the update is about.
ID uint64
// Run names the adaptive run this row belongs to. Empty means ordinary work.
Run string
// Node names the adaptive node inside Run. THE RUN'S OWN ROW HAS NO NODE.
Node string
// Title is the one-line name of the work ("Fix the nil-map crash").
Title string
// Kind is what sort of node this is, and "" is the ordinary one: work in a
// worktree. It is on the proposal AND on every update, because it is the one
// fact about a node that is true before it starts and after it lands.
Kind TaskKind
// Program is the program codeaf carries that this work is handed to —
// senior-dev — by the one name that program answers to (the Name of its
// [delegate.Delegate], the word its command row says), and "" for every task
// a worker of this conversation's own does, which is almost all of them.
//
// IT IS ON THE PROPOSAL AND ON EVERY ROW A PROGRAM'S RUN PUBLISHES, and that
// is the whole of why it is here. A surface used to learn a program's name
// only from the run's plan rows, which it reads on a beat of its own and
// drops on a conversation switch — so the card a person answered could not
// say which program the work was going to, and a program's row on the side
// list looked exactly like an ordinary task's for its first seconds and again
// after every switch. Carried here, the badge a program's work wears
// (internal/tui3's programbadge.go) is there from the first frame.
//
// IT IS A FACT FOR THE ROW'S WHOLE LIFE, like Kind above it: settled before the
// work starts and moved by nothing that happens to the work afterwards, so a
// publisher that forgets it has not changed it ([Agent.publishRunRow] carries
// it forward).
Program string
// Ceiling is the finite allowance a proposed program run will start with,
// spelled for the approval card. Ordinary tasks leave it empty.
Ceiling string
// Summary is the two-or-three-line gloss the person scans to decide.
Summary string
// Brief is the node's WHOLE context: the goal, every fact the chat knew
// that the work needs, the files, the conventions, the acceptance test —
// and, once graphs have edges, whatever its prerequisites' reports
// taught. The node never reads this session; the brief is the contract.
Brief string
// Acceptance is the observable done-condition, in the model's own words.
Acceptance string
// Where says where the task will work. A proposal carries the resolved task
// folder or explicit path; later notices carry the worker's actual directory.
Where string
// Ground is the repository or folder THE WORK IS ABOUT, absolute, and Mode is
// how the task stands on it (taskstands.go resolves both). Where says which
// directory the worker types in; these two say which project that directory is
// a copy of, which is the fact a person needs to read before they approve
// anything — a card that named only a task folder under a session was telling
// somebody where the machinery was, never where their work was going.
//
// They are on the PROPOSAL and on every update, because the answer is settled
// before the countdown starts and never moves afterwards. Empty Ground is the
// emptiness law and not a claim: a notice written by a door that never
// resolved one has nothing to say about it.
Ground string
Mode TaskMode
// Rung is WHICH COPY OF THE GROUND the work is being done in — the rung of
// the ground ladder that made this node's world (groundladder.go). Where and
// Ground say which directory and which project; this says what that directory
// IS, which is the fact a surface needs before it can name the place in a
// person's words ([GroundWord] holds the table).
//
// IT IS EMPTY UNTIL THERE IS A WORLD TO DESCRIBE. A proposal has not been
// carved yet and a node given a folder of its own on the reference promise
// climbed no rung at all, so both leave it unset — the emptiness law, and not
// a claim that the work happened nowhere.
Rung GroundRung
// DependsOn names the nodes that must finish before this one may start —
// IDs of sibling proposals. Empty in a one-node graph.
DependsOn []uint64
// Parent is the work this one was SPAWNED UNDER, and 0 is a root — which is
// every task a person or the model proposed. It is filled by an adaptive run,
// whose nodes are registered as a family under one row for the run itself
// (orchestrate.go's family seam), and it is what a roster draws a tree from.
//
// IT IS NOT A DEPENDENCY. DependsOn says what must finish first; this says
// who asked for the work. A run's two independent nodes share a parent and
// have no edge between them, and collapsing the two would draw a tree that
// says the wrong thing about what is waiting for what.
Parent uint64
// Deadline is when silence becomes approval — now plus the configured
// countdown (task.autoapprove_seconds). A zero Deadline means the clock is
// off or has been held by typing, and only an answer resolves the proposal.
Deadline time.Time
// Withdrawn is set on the one rebroadcast of a proposal that takes it back
// before anybody's answer admitted it, and it is the reason in the engine's
// words. It happens to exactly one kind of proposal: one whose card went up
// while the message carrying the call was still arriving, when that message
// then did not go through (task.go's [Agent.stageTask]). A surface settles
// the card with it — whatever its own clock has drawn by then — because
// nothing was started and nothing is waiting on the card any more.
Withdrawn string
// Decided is what somebody said about this proposal, on the one rebroadcast
// that goes out the moment anybody does — the person, another window, or the
// countdown — and nil while it is still a question.
//
// IT EXISTS SO A CARD CAN BE REPLAYED WITHOUT BEING ASKED AGAIN. A turn's
// backlog is handed whole to whoever attaches next ([eventHub.attach]), and
// the card that raised a question is the one event in it that is not a report
// of something that happened. The open card is left out of that replay once
// its question is settled; this one goes in its place, so a person who
// approved a task, looked at another tab and came back reads the assignment
// with their own answer under it rather than the question a second time.
//
// A surface draws the verdict from Approved and Redirect together, which is
// the same reading the window that answered already made of its own key. The
// CLOCK's own wording is that window's and is not restated here: what is true
// afterwards is that the work was approved.
Decided *TaskAnswer
// ModelOptions is the shortlist a `model` argument raised that fits more
// than one model this install has (taskmodel.go). It is empty for every
// ordinary proposal — one word, one model, nothing to ask — and when it is
// set, Model is its leading member: the closest match, what the card shows,
// and what the clock settles on if nobody picks. A surface offers these for
// the person to confirm and hands the chosen one back on [TaskAnswer].
ModelOptions []string
// Elsewhere is ONE LINE about the work other windows on this project already
// have out in the files this brief names, and "" when there is nothing to say
// (taskpreflight.go writes it and spells the words).
//
// IT IS A FACT ON THE CARD, NOT A GATE. The countdown runs the same, the
// options are the same three, and a person who ignores it gets exactly the
// task they were shown. Empty is the emptiness law and NOT a claim of
// clearance: a brief that named no files and a live node that has not written
// yet both leave it empty, so a surface must draw nothing rather than "you are
// alone in these files".
Elsewhere string
// State is queued, running, done, failed or unverified.
State TaskState
// Elapsed is the node's age at this update.
Elapsed time.Duration
// StartedAt is the record's fact of when this node started. It is the zero
// time when nothing recorded one, and every surface draws that as nothing.
StartedAt time.Time
// EndedAt is the record's fact of when this node landed. It is the zero time
// when nothing recorded one, and every surface draws that as nothing.
EndedAt time.Time
// Report is the done/failed story in two or three lines: what it did, or
// what stopped it. It is the card — a row on the roster, the head of a
// landing note — and it is cut to fit one.
Report string
// Result is what the work actually produced, as much of it as one reader's
// context is handed ([taskResultCarry]); ResultCut says that is only the
// beginning of it, and ResultWhole is where the whole can be read
// (task_result.go).
//
// ResultHeld is the landing that turned the work back: the answer is NAMED
// rather than handed on — Result is empty, ResultWhole says where it is, and
// nothing recycles an account the check did not accept.
//
// They are empty on a landing whose report already carries the answer exactly,
// which is every task that finished in two or three short lines, and on work
// that has not landed. A surface that ignores them draws what it always drew.
Result string
ResultWhole string
ResultCut bool
ResultHeld bool
// Changed lists the files the node wrote, repo-relative.
Changed []string
// Branch is the task's branch ("task/fix-nil-map"), kept after a protected
// landing, a conflict or a kill so the work is never silently thrown away.
Branch string
// Copy is WHERE THE WORK HAPPENED, written down so a later process can find
// it again rather than make it again (task_run_copy.go). It is set on a
// RUN's row and nowhere else: a node of the session's own graph already
// carries these facts in its own record. Nil is a row whose copy was never
// recorded, which is a run that cannot be carried on.
Copy *TaskCopyRecord
// PendingRun keeps an admitted request before it owns a working copy.
PendingRun *PendingRunRecord
// PlanTask is WHICH TASK OF THE PLAN STORE THIS ROW IS, and it is the one
// fact that tells a row the store answers for from a row the graph holds a
// node for. It is set on a RUN's row and nowhere else, by the door that
// minted both halves in one breath (task_run_belt.go's
// [Agent.startKnownTaskRun] names the store task with the number the row
// wears), and it is empty on every node of the session's own tree.
//
// IT IS SPELLED THE ONE WAY A STORE ID CROSSES THIS SEAM ([planStoreID]):
// the same spelling [PlanTaskRow.ID] carries and [Agent.PlanTaskPage] is
// asked for. The store's own bare id is answered under by nothing a surface
// can reach, so a row carrying that instead would name an identity no read
// in this package joins.
//
// IT IS AN IDENTITY AND NOT A DESCRIPTION. A surface reading it knows this
// row and that store task are one piece of work read from two ends, so it
// can draw the one of them the store is the authority for — its state word,
// and the page carrying its worker's trajectory. Before this field existed
// the only link was the TITLE the two halves happened to share, which
// cannot tell a run's row from a node that merely wears the same words, and
// the place drew the row whose Enter opened a room the engine holds no node
// for (internal/tui3's taskplan.go).
PlanTask string
// Merge is how the branch came home: "merged", "kept" (finished but left
// on its branch), "conflicted" (branch kept), "inplace" (a non-git
// workspace ran in the person's tree), or "" while running.
Merge string
// Doing is the PHASE a running node of a named kind is in, in that kind's
// own plain words — "designing", "awaiting your look" for a subharness
// being written (harness_task.go) — and "" for an ordinary task, which has
// no phases.
//
// A SURFACE DRAWS IT INSTEAD OF THE STATE WORD, which is what separates it
// from Mending and Waiting below: those two are said BESIDE "running",
// because the node is running and hiding that would hide the state. This
// one IS the state, said in the vocabulary of the work rather than of the
// machinery — "designing" is what a person would call it, and "running" is
// what this package calls it. Like the two below it is ANNOUNCED ON CHANGE:
// a phase moving is news that arrives without the state moving.
Doing string
// Context is the NAMED WORKING CONTEXT this node's room is — what a person's
// own words in it are part of, in their own words: "designing a subharness",
// and then "designing subharness flake-triage" once the page has a name (harness_task.go).
// It is "" for an ordinary node, whose room is work being watched rather than
// a thing somebody is inside.
//
// IT IS FOR THE PERSON'S OWN LINE AND NOT FOR THE NODE'S ROW. Doing above says
// what the work is busy with and belongs on a roster; this says what a turn
// somebody takes in here RUNS INSIDE, and belongs where that turn starts — a
// transcript that draws the person's `›` line identically whether the words
// went to a conversation or into a design thread is a transcript that cannot
// be read back (internal/tui3's turncontext.go).
//
// A SURFACE DRAWS IT VERBATIM AND KEEPS NO LIST OF CONTEXTS. The name is the
// node's own to write, so a kind of node that is also a place somebody talks
// inside becomes visible everywhere by filling this and nowhere else. Empty
// draws nothing at all, which is the emptiness law and is what every ordinary
// turn on every surface gets. Like Doing it is ANNOUNCED ON CHANGE: a context
// that has just learned its name is news that arrives without the state moving.
Context string
// Mending is the gap being closed while a repair round runs, one plain
// line ("adding amp-labs to the report"), and "" at every other moment.
// A surface draws it as the task simply still working; the machinery
// that sent it back is not the surface's to mention.
Mending string
// Waiting is why a QUEUED node is not running yet, or why a RUNNING one is
// paused mid-call, one plain word or two: "" (nothing to say), "slot" (a
// task.parallel cap holds it), "machine busy" (the admission governor
// holds it), "rate limited" (the provider is pacing it). A surface draws
// it as the queue telling the truth; the machinery behind it is not the
// surface's to name. Like Mending it is ANNOUNCED ON CHANGE: a hold
// starting and a hold ending are both news that arrives without the state
// moving, so an update carrying only this is still one a surface folds in.
Waiting string
// Paused says this row is HELD AT A GATE only a person can open: an adaptive
// run that has spent its tank and stopped launching, waiting to be topped up,
// finished or stopped ([EventOrchestratePause], [Agent.ResolveOrchestrate]).
//
// IT RIDES BESIDE `running` RATHER THAN REPLACING IT, on Stopped's own terms.
// The run is running as far as the run is concerned — whatever was in flight
// when the tank emptied is still working, and those rows still say so — and it
// is not moving as far as a person is concerned, and the second reading is the
// one a roster owes them. ONLY THE RUN'S OWN ROW EVER CARRIES IT: a worker
// under a paused run is not paused, it is finishing what it started.
//
// IT IS A REPORT OF RIGHT NOW, like Mending and Waiting, and like them it is
// ANNOUNCED ON CHANGE: the gate going up and the gate being answered are both
// news that arrives without the state moving. Every row the run publishes
// while the gate is up carries it, so a lane that opens late is told exactly
// what a live watcher was (orchestrate.go's [orchestrateFamily.publish] is the
// one place it is written).
//
// IT IS A FACT OF THIS PROCESS AND NOT OF THE CHECKPOINT. Nothing resumes a
// run across a restart, so a restored row comes back settled and never paused
// (task_store.go's [runRecord]).
Paused bool
// Settling names the RESOLUTION IN FLIGHT over a landed node, in the plain
// words the claim was taken in — "your accept", "your refute", "a
// re-audit" (task_audit.go's [Agent.ResolveUnverified]) — or "a merge
// round" while a conflict round runs (task_merge_round.go's
// [TaskNode.claimResolving]). It is "" at every other moment.
//
// A SURFACE DRAWS NOTHING FROM IT. It rides the notice so the question
// machinery can hold a landed node's question down while the answer's own
// work is still running — the accept whose merge is still deciding, the
// re-audit still spending its window — rather than ask it again with no
// new fact (task_landing_question.go's [Agent.publishLandingQuestion]).
//
// IT IS A REPORT OF RIGHT NOW, like Mending and Waiting: the graph's own
// claim fields, copied onto the notice at the one place node-update
// notices are built (task_run.go's [TaskNode.noticeLocked]), never stored
// beside them. And it is A FACT OF THIS PROCESS AND NOT OF THE CHECKPOINT:
// a restored session has no resolution in flight, so a zero value after a
// restore is the truth and not a gap.
Settling string
// Stopped says a PERSON ended this node ([Agent.Cancel]) rather than the
// work ending on its own. It rides beside State rather than replacing it —
// a stopped node still settles as `failed`, because nothing merged and its
// dependents still cannot be briefed — and it exists because "failed" and
// "you stopped it" are different news about the same state: one sends
// somebody looking for a fault, and the other is the fault.
//
// IT IS A FACT OF THIS PROCESS AND NOT OF THE CHECKPOINT. A session resumed
// from disk knows the node failed and does not know who ended it, so a
// surface reading this draws the stop while it can and falls back to the
// failure afterwards.
Stopped bool
// Ending is WHY a node that did not finish stopped where it did, in one word
// a surface can draw a row from ([TaskEnding]). It is set on a node that
// settled `failed` — and on a node MACHINERY CUT where it stood, which stays
// running for recovery to resume and carries [TaskEndingInterrupted] so the cut
// is not silent — and is "" on every other state, and on every failed node
// checkpointed before the field existed, which a surface draws exactly as it
// always did. It exists because every ending that keeps a branch used to wear
// the one sentence "stopped — branch kept", and six rows of that on a rail
// were, when the records were read, a connection that dropped, a worker that
// gave up going in circles, a check that did not accept the work, and not one
// person pressing stop.
Ending TaskEnding
// Conflicts names the files that CLASH, on a landing whose branch would not
// fasten onto the person's — read out of the index while the refused merge
// still held them (task_run.go's [conflictSentence] writes the sentence from
// the same list). It is empty on every landing that did not conflict, and
// empty on one where git would not say which files it was about, which is the
// emptiness law and not a claim that nothing clashed.
Conflicts []string
// Shifted says THE GROUND MOVED rather than the merge failing: the branch
// would have fastened, and the person's own branch changed the same files
// while this node worked (taskground.go, task_run.go's [Agent.landShifted]).
// The question is the conflict's question either way — two versions of these
// files, which survives — and this is the fact that decides which of the two
// sentences a row reads, so that nothing has to tell them apart by their
// prose.
Shifted bool
// GroundHeld says the landing was refused by THE PERSON'S OWN UNTRACKED COPIES
// of the files this task wrote, sitting in the folder the branch merges into
// (groundcarry.go). It is the third road to the conflict's one question, and
// it is the road whose `resolve it` carries those copies aside rather than
// spending a merge round: a file git is not watching is on no branch, so
// there is nothing for a round to merge.
GroundHeld bool
// Decider is WHO HOLDS THIS NODE'S DECISION right now ([TaskAskOwner]). It is
// the person on every ordinary landing; `task.settle = auto` and a person
// pressing "let codeaf decide this one" ([Agent.HandUnverifiedToModel]) are the
// two things that make it the model, and neither of them makes it the model for
// long — the floor hands it back when the model's turn ends (agent.go).
//
// A CONFLICT IS NEVER THE MODEL'S, whatever the policy says: it cannot merge by
// decree (task_run.go's [Agent.handToModelOnAuto]).
Decider TaskAskOwner
// Checked is WHAT THE CHECK SAID about this node's work, and "" on a node no
// check ever read. It is narrower than State on purpose: a node taken as it
// stands, one landed with the check switched off and one a person accepted
// are all done and none of them was judged (taskgrade.go's
// [TaskNode.checkSaid]).
Checked provider.Reading
// Model is the model this node runs on: the one the proposal named, the
// configured task model, or the conversation's own (taskmodel.go). It is on
// the proposal AND on every update, because it is a fact about the work that
// outlives the question — a card that lands twenty minutes later still says
// whose hands did it.
Model string
// NextModel is a saved continuation choice; Model still names the last attempt.
NextModel string
// Crew is the crew the router picked for this task — the class it read the
// task as, each seat's model and route, which seats were pinned, and the
// estimate — and nil on work that was not routed (taskcrew.go). The card
// draws its crew line from it, with CostUSD as the actual beside the
// estimate once there is one.
Crew *crewroute.Decision
// CrewState is the private checkpoint policy; surfaces draw Crew instead.
CrewState *TaskCrewRecord `json:"-"`
// CostUSD is what this node's own agent has spent, live while it runs and
// frozen once it lands. Zero means nobody published a price — an unpriced
// model, or a node that has not started — and it is NOT the same claim as
// "it cost nothing", so a surface draws no figure at all for it.
CostUSD float64
// Tokens is what this node has burned, input plus output, read the same
// way as CostUSD: what its folded hands spent, plus the worker still in the
// room ([TaskNode.burned]). It is the one figure a surface with no lane to
// the worker — a window on another machine — can draw a node's tokens from.
// Zero is "nobody counted", and absent from rows that did not carry it (the
// roster replay, a row from a build before it existed).
Tokens int
}
TaskNotice is the flat payload of EventTaskProposal and EventTaskUpdate — one struct for both, the way Event itself is one struct: a proposal fills the top half, an update the bottom, and no surface reads a field its kind did not set.
func (TaskNotice) StatusFacts ¶
func (n TaskNotice) StatusFacts() TaskFacts
StatusFacts is one live notice as the reading takes it, so that the surface drawing a card, the note the model reads and the roster all ask the same function the same question about the same node.
IT CLAIMS NOTHING THE NOTICE DOES NOT CARRY. A notice has no consent clock, no fuel figure, no prerequisite TITLES and no word for which of a running node's three lives it is in — those reach the reading from whoever is holding them (the surface's own EventTaskPhase fold, the proposal card's countdown) — and every one of them stays an absence here rather than a guess.
type TaskPhaseNotice ¶
type TaskPhaseNotice struct {
// ID names the node, and is the same id its [TaskNotice] carries: a surface
// folds this into the row it already drew rather than opening a second one.
ID uint64
// Phase is one of the words above.
Phase string
// Round and Rounds are which repair round this is and how many the person's
// settings allow ("round 1 of 1"). They are set on TaskPhaseRepairing alone
// and are zero on the others, which is the emptiness law: a surface draws
// no numbers at all for a check, because a check has no rounds.
Round int
Rounds int
// Text is the check's finding in ONE line, in a person's words, and it rides
// the repairing phase because that is the moment it becomes true of the work:
// the check did not accept it, and here is what it said. It is "" everywhere
// else, and "" is drawn as nothing.
//
// It is the checker's own sentence with a plain-words opener in front of it
// ([mendingLine], [taskFindingLine]) and never a paraphrase — the machinery's
// names for what happened are not on this wire.
Text string
// Call is THE REQUEST THIS PHASE IS WAITING ON, while it is out, and nil
// the rest of the time — before the first request goes, between two rungs
// of a ladder, and on every phase that runs no call of its own
// (task_calltrail.go).
//
// IT RIDES THE PHASE AND NOT A LANE OF ITS OWN because it is the phase's
// own news: the sizing word says the work is being sized, Text says which
// model was asked, and this says what that model is doing right now. One
// notice carries all three so that a surface copying the notice whole — as
// every one does — can never draw a live call under a phase that has moved
// on.
Call *TaskCall
}
TaskPhaseNotice is the payload of EventTaskPhase: which node moved, which of the words above it moved into, and — while a repair round runs — which round out of how many.
IT EXISTS BECAUSE A RUNNING NODE IS NOT ONE THING. The state stays `running` across a worker, a check and every repair round, so a surface holding only EventTaskUpdate draws the same row for a node writing code and a node that finished writing code eight minutes ago and has been under a check ever since. That gap is what makes a person conclude the work hung: the worker's last line scrolls past, and then nothing at all is drawn for minutes while the check reads the tree and a repair round rewrites it.
IT IS NEWS, NOT A ROW. It carries no state, no elapsed, no cost — an update is where those live and this never contradicts one. A surface that ignores this kind is exactly the surface it was.
type TaskPresence ¶
type TaskPresence string
TaskPresence is where one task is in the person's terms. The zero value means nothing is known — an unrecognised state is not evidence that anything was admitted or run.
const ( // TaskPresenceUnknown draws no row: there is no reading. TaskPresenceUnknown TaskPresence = "" // TaskPresenceQueued is admitted and not started. [TaskStatus.Reason] may say // what is holding it. TaskPresenceQueued TaskPresence = "queued" // TaskPresenceWorking is a worker spending its time on the work. TaskPresenceWorking TaskPresence = "working" // TaskPresenceWaiting is admitted work that is not being worked on right now // because something else has to happen first; [TaskStatus.On] says what. It is // the difference between "stuck" and "next in line", which a still row cannot // otherwise show. TaskPresenceWaiting TaskPresence = "waiting" // TaskPresenceFinishing is the end of a run: the check reading what the worker // left, or a round closing named gaps. TaskPresenceFinishing TaskPresence = "finishing" // TaskPresenceDone is work that ran to the end and was accepted. It says // nothing about where the edits went; that is [TaskStatus.Changes]. TaskPresenceDone TaskPresence = "done" // TaskPresenceIncomplete is work that ended without finishing: a check that // named gaps, a dropped connection, a threshold, a brief whose world had // moved, or a fault. Only [TaskStatus.Fault] says something went wrong. TaskPresenceIncomplete TaskPresence = "incomplete" // TaskPresenceNeedsLook is work the machine has taken as far as it can — a // claim nobody could check, a design waiting to be approved. TaskPresenceNeedsLook TaskPresence = "needs-look" // TaskPresenceStopped is a person ending the work, and nothing else. A // threshold, a loop guard and a rule the worker would not follow also end // runs, and none of them is this. TaskPresenceStopped TaskPresence = "stopped" // TaskPresenceInterrupted is work NOTHING IS DRIVING, whose every step is // kept. The window closed, the machine slept, the engine died: none of those // is a finding about the work and none of them is a person's decision, so // none of them may read as stopped or incomplete. // // IT IS NOT A SETTLED READING. The work is not over — it is waiting to be // picked up — which is the whole of what this rung says that the two beside // it cannot. `stopped` stays a person ending the work, `incomplete` stays // work that ran and came up short, and reading either over work whose only // misfortune was a closed window is what this rung exists to stop. TaskPresenceInterrupted TaskPresence = "interrupted" )
type TaskRecord ¶
type TaskRecord struct {
// Report is the last thing the node said, cut the way [PeekReport] cuts it,
// and empty where the journal holds no report of its own.
Report string `json:"report,omitempty"`
// Kept says the journal the row names is STILL ON THE DISK OF THE MACHINE
// THAT RAN THE WORK.
//
// IT IS A FACT AND NOT AN INFERENCE FROM AN EMPTY REPORT. A session folder
// somebody deleted and a journal that never held a report are two different
// sentences on a card, and only the machine holding the file can tell them
// apart — a surface that guessed from an empty string would say "its
// transcript is not on this disk any more" about a file that is sitting
// perfectly well on the other one.
Kept bool `json:"kept,omitempty"`
// Journal is the END of the node's own transcript, whole lines, and nil
// whenever the caller did not ask for any.
//
// IT IS BYTES AND NOT A PARSED PAGE. The journal's framing is one JSON object
// per line and the surface already has the reader for it (internal/tui3's
// readJournalLines), so a shape invented here would be a second parser that
// has to agree with that one for ever — and Decision 1's whole bargain is
// that a payload is the thing itself rather than a translation of it.
//
// IT IS A TAIL AND NEVER THE FILE. A node's transcript is megabytes and the
// page that draws it keeps the last screenful of blocks anyway
// ([ReadTranscriptBytes], shaped by tui3's roomReplay), so sending the whole of one would be paying a
// connection for lines nothing will draw. The cut is made at a LINE BOUNDARY
// — the first newline after it is dropped along with the partial line before
// it — because a torn first line is a line the reader throws away and a
// caller cannot tell that from a line that was never written.
Journal []byte `json:"journal,omitempty"`
// Beat is the node's pulse sidecar as the record itself names it
// (task_store.go's taskRecord.Beat, written by task_beat.go), and "" for
// every node that is not running one. IT IS THE RECORD'S OWN NAME FOR THE
// FILE AND NEVER A PATH THIS SIDE COULD BUILD: a reader that recomputed it
// from an id would be a second spelling of where the pulse lives, and the
// whole bargain of the field is that whoever holds the record never has to
// guess. Over a connection the file is on the machine that ran the work, so
// the path crosses as data and is opened by whoever can reach it; empty on
// the far reader is the honest answer and not a failure.
Beat string `json:"beat,omitempty"`
}
TaskRecord is that reading: what the card knows about one piece of work beyond the row it was opened from.
IT CROSSES THE WIRE AS ITSELF (internal/remote's Decision 1), because over a connection the journal is on the machine that ran the work and this surface has no way to open it — which is the whole reason the type exists rather than the card simply calling PeekReport on a path.
func ReadTaskRecord ¶
func ReadTaskRecord(uri string, tail int) TaskRecord
ReadTaskRecord reads the journal one row names, for its last word.
IT MAKES NO DECISION ABOUT WHOSE DISK THIS IS. A surface reading its own machine's record calls it directly; an engine answering for its machine over a connection applies its own boundary first (ReadTaskRecordUnder), which is the same division internal/remote keeps everywhere else — the reading is one function and the permission is the engine's to make.
TAIL IS HOW MANY BYTES OF THE JOURNAL'S END TO CARRY BACK, and zero — the card's own answer — carries none. A room wants the transcript and a card wants one paragraph of it, and the difference between the two is a megabyte on a wire, so the caller says which it is asking for rather than every caller paying for the larger.
func ReadTaskRecordUnder ¶
func ReadTaskRecordUnder(roots []string, uri string, tail int) (TaskRecord, error)
ReadTaskRecordUnder is that reading with the one boundary an ENGINE has to apply: the journal must be a file under one of the roots this machine answers for (RecordRoots).
THE PATH CAME FROM HERE IN THE FIRST PLACE. A remote surface only ever asks about a URI this machine wrote into its own index and handed over on the world walk — but a door that trusted that would be a permission decision taken on the strength of what the other end says, so the roots are checked here, on the machine that owns them. It is internal/remote's two-roots law restated for the directories this door answers about.
IT TAKES A LIST BECAUSE THE RECORD IS IN MORE THAN ONE PLACE, and taking one root was a bug and not a simplification: an adaptive run's node journals live under the runs root, so a door holding only the places root refused every one of them — the file was on the disk, this machine had written its path into its own index, and the surface was told it could not be read.
type TaskReplyTag ¶
type TaskReplyTag struct {
ID uint64 `json:"id"`
Title string `json:"title"`
Request string `json:"request,omitempty"`
// Obligation is WHAT THIS RESULT WAS ACTUALLY OWED when it was delivered,
// and it is set only when the person moved the goal while the work ran: the
// admitted ask as history, their applied directions in order, and the
// deliverable and done-condition as they then stood (wakecause.go's
// [obligationText], from the assignment's own snapshot). Revision is the
// assignment version it was taken at, and 0 on an unrevised task.
//
// A REVISED TASK IS JUDGED BY THIS AND CITED BY Request. Request stays the
// person's original words for the row a surface draws beside the answer;
// judging a CSV result against the JSON that was first asked for is the
// failure these two fields exist to prevent ([Agent.turnAsk] reads them).
// Both are optional: an old tag, or one from an unrevised task, carries
// neither and is read exactly as it always was.
Obligation string `json:"obligation,omitempty"`
Revision uint64 `json:"revision,omitempty"`
}
TaskReplyTag is the task identity a surface places beside the answer its completion prompted. Request is the person's original text, not the brief.
type TaskResolution ¶
type TaskResolution string
TaskResolution is what a person decides about a node no auditor could judge.
The three are the only three answers there are to "nobody could verify this": say it holds, ask again, or say it does not. Each lands the node in one of the states above — done, unverified again, failed — through the same settle the gate itself uses, so a resolved node is indistinguishable afterwards from one that reached that state on its own.
const ( // TaskAccept takes the work as done on the person's word: the branch comes // home exactly as a VERIFIED one would, and the dependents unblock. TaskAccept TaskResolution = "accept" // TaskReaudit sends a fresh auditor at the same working copy. The node stays // unverified until that verdict lands. TaskReaudit TaskResolution = "reaudit" // TaskRefute is the person doing the auditor's job in the negative: the node // fails, its branch is kept, and the cascade takes its dependents. TaskRefute TaskResolution = "refute" )
type TaskRollup ¶
type TaskRollup struct {
// Rows are this session's entries, newest first, exactly as
// [ReadTaskIndex] returned them.
Rows []TaskIndexEntry
// Running is work the index calls running or queued AND THE SESSION ITSELF
// STILL NAMES as out — the only rows this layer will call live. See
// [rollUp], which is the one place that judgement is made.
Running int
// Incomplete is every other live-looking row: work that was under way when
// the window went, or that the conversation no longer counts among what it
// has out. The word is the one the interrupted-task outcome already uses,
// because it is the same fact.
Incomplete int
// Done and Failed are the landed rows, counted by what they came to.
Done int
Failed int
// Spend is the sum of the rows' cost, in dollars, and Tokens the sum of
// what they weighed. Zero means nobody could say, and under the emptiness
// law a surface draws nothing for either.
Spend float64
Tokens int
// Newest is when the most recent of these rows landed, and zero when the
// only rows are ones that have not.
Newest time.Time
}
TaskRollup is one session's share of its project's index, counted.
type TaskSettle ¶
type TaskSettle string
TaskSettle is the person's standing answer to "who decides a task nobody could check" — the `task.settle` row (internal/config's settings.go).
IT IS A POLICY AND NOT A CAPABILITY. Both values leave the model the same `tasks … resolve` verb and leave the person the same choices on the landed card; what changes is who is ASKED first, which is the whole of what a person is annoyed about when a third task in an afternoon lands waiting on them.
const ( // TaskSettleAsk puts the decision in front of the person: the landing note // tells the model what happened and what the choices are, and the model does // not spend a decision on their behalf. It is the default. TaskSettleAsk TaskSettle = "ask" // TaskSettleAuto hands the decision to the model: the landing note tells it // to read the report and the work and settle the node itself, and to come // back to the person only when it genuinely cannot tell. TaskSettleAuto TaskSettle = "auto" )
type TaskState ¶
type TaskState string
TaskState is where one node is in its life.
const ( // TaskQueued says the node is admitted but its dependencies are not all // done — it waits on the frontier. A one-node graph never sits here. TaskQueued TaskState = "queued" // TaskRunning says the child agent is working in its worktree. TaskRunning TaskState = "running" // TaskDone says the run finished and the report is in. TaskDone TaskState = "done" // TaskFailed says the run ended without finishing — an error, a kill, or // a merge the runner would not guess at. Report says which. TaskFailed TaskState = "failed" // TaskUnverified says the run finished and NOBODY COULD SAY whether the // work holds: the auditor answered with neither verdict word, or the audit // call itself never came back (task_audit.go). It is a settled state — the // run is over, the slot is handed back, the branch is kept — and it is // deliberately NOT TaskFailed. // // "The work is wrong" and "nobody could tell me whether the work is wrong" // are different news with different consequences, and collapsing the second // into the first is how a broken auditor fails good work and then fails // everything downstream of it. So nothing CASCADES from here: a dependent of // an unverified node stays queued rather than failing, because an unverified // claim is not evidence and is also not a refutation. What moves it is a // person — [Agent.ResolveUnverified], reachable from the `tasks` tool — accepting // the work as done, asking for another audit, or refuting it themselves. TaskUnverified TaskState = "unverified" // TaskInterrupted says NOTHING IS DRIVING THE NODE and every step it took is // kept. The window closed, the machine slept, the engine died. It is settled // in the scheduler's sense — no worker holds it, the slot is back — and it is // deliberately NOT TaskFailed, because nothing was found out about the work. // // "The work did not finish" and "nobody was there to carry it on" are // different news with different consequences, and collapsing the second into // the first is how a run whose window was closed came back reading as though // it had gone wrong. Nothing here moves it on its own, because continuing // spends money; and nothing a person can press moves it yet either, so the // row asks no question and raises no mark (task_status.go's // [taskInterruptedReason]) until the card that carries a run on lands. TaskInterrupted TaskState = "interrupted" )
type TaskStatus ¶
type TaskStatus struct {
Presence TaskPresence
On TaskWaitOn
// Reason is the engine's own prose about why — the hold, the gap, the
// prerequisites by name, a kind's phase word. This file never writes a
// sentence of its own into it.
Reason string
// Fault says something went wrong, as distinct from work that did not finish:
// a dropped connection and an exhausted threshold are not faults.
Fault bool
// Attention says the node will not move without a person.
Attention bool
// Changes and Branch are the source-control axis.
Changes TaskChangeDisposition
Branch string
// State, Ending and Liveness are carried through unchanged, so a caller that
// needs the runtime's own facts reads them instead of inferring them back out
// of the presence.
State TaskState
Ending TaskEnding
Liveness TaskLiveness
// Tier is the one question a person asks of a row before anything else, and
// Word is the word the row wears for it. Ask is the question and its two
// answers, and it is the zero value unless Tier is [TaskTierYourCall].
//
// A SURFACE READS THESE AND SPELLS NOTHING OF ITS OWN. The glyph comes from
// the tier, the row from Word and Reason ([TaskStatus.RowWord]), the card from
// Ask. Presence and On are still here and still say where the work is; what
// they no longer decide is what a person is told.
Tier TaskTier
Word string
Ask TaskAsk
}
TaskStatus is the reading. Each field answers a different question, and none is derivable from another.
func ProjectTask ¶
func ProjectTask(facts TaskFacts) TaskStatus
ProjectTask reads one node's facts, and answers the question every surface asks first: which tier, which word, and — when it is the person's call — which question and its two answers.
The order of the tests is the design: work nobody has agreed to yet outranks everything, because there is no lifecycle to read on a proposal; a person's stop outranks the state, because a node they ended settles `failed` and nothing went wrong with it; and an authoritative "nothing holds this" outranks a claim of running. After those the lifecycle leads, and within a state the most specific true fact wins. The words go on last, over the reading rather than inside it ([taskStatusWords]).
func (TaskStatus) ChangesUnlanded ¶
func (s TaskStatus) ChangesUnlanded() bool
ChangesUnlanded reports that the node's edits sit on a branch that never came home. It is a source-control claim only.
func (TaskStatus) RowWord ¶
func (s TaskStatus) RowWord() string
RowWord is the whole of what one row says: the word, and the reason under it where there is one.
A ROW NEVER READS A BARE `waiting` OR A BARE `your call`. The reason is the half a person can act on, so it travels with the word everywhere the word is drawn. THE REASON IS NEVER SAID TWICE: a word that already carries its own object ("waiting on Collect sources") is the whole sentence on its own.
func (TaskStatus) Settled ¶
func (s TaskStatus) Settled() bool
Settled reports whether the run is over, from the lifecycle state alone. A design waiting to be approved needs a person and is still running work: its room stays open and its stop still works, so terminality may not be read off the presence.
type TaskTier ¶
type TaskTier string
TaskTier is the ONE QUESTION every row answers before it says anything else: do I need to do anything? There are three answers, each with one glyph and one word, and a person who has learned three glyphs has learned the whole system (docs/design/task-states/DESIGN.md).
IT IS COMPUTED HERE AND NOWHERE ELSE. Every surface that worked a tier out of the state, the ending, the stop flag and the merge word grew a table of its own, and the tables disagreed — which is the defect this type exists to end.
const ( // TaskTierMoving is work in flight: nothing for you. TaskTierMoving TaskTier = "moving" // TaskTierOver is work that has ended, however it ended: nothing for you, and // a rerun may be offered. TaskTierOver TaskTier = "over" // TaskTierYourCall is the machine having done what it can. The card carries // the reason and the two answers. TaskTierYourCall TaskTier = "your-call" )
type TaskWaitOn ¶
type TaskWaitOn string
TaskWaitOn is what a waiting task is waiting on. "Waiting" alone leaves the person's next move undecidable.
const ( TaskWaitNobody TaskWaitOn = "" // TaskWaitPerson is the person's attention. TaskWaitPerson TaskWaitOn = "you" // TaskWaitWork is unmet prerequisites, named in [TaskStatus.Reason] when the // caller could resolve them. TaskWaitWork TaskWaitOn = "work" // TaskWaitMachine is capacity or a provider — a slot, a busy machine, paced // calls — and it clears without anybody doing anything. TaskWaitMachine TaskWaitOn = "machine" )
type TeamLine ¶
type TeamLine struct {
// Team is the team's name as the delivery named it.
Team string
// From is "manager", a member's handle (no @), "you" or "system".
From string
// To is where the line was aimed: "room", "everyone", "manager" or a
// member's handle, and "" for the conversation reading it.
To string
// Kind is [teams.KindNote], [teams.KindDirective], or [teams.KindStart] for
// the brief a manager started this conversation with.
Kind string
// Text is the line's words, whole lines kept.
Text string
// Thread is the line's own entry id when the delivery numbered it ("#42",
// which a member is told so its reply can name it), "" when it did not.
Thread string
}
TeamLine is one line of team traffic as a conversation was handed it.
type TeamProposal ¶
type TeamProposal struct {
New []ProposedTeam
Additions []ProposedAddition
Model string
PromptChars int
}
TeamProposal is one validated answer. Model is the model that answered and PromptChars the size of what it was asked, so the surface can say about what the ask cost.
type TeamProposalConversation ¶
TeamProposalConversation is one conversation offered to the model: the key the surface knows it by, its title and its project folder.
type TeamProposalInput ¶
type TeamProposalInput struct {
Conversations []TeamProposalConversation
Teams []TeamProposalTeam
}
TeamProposalInput is everything one ask is about.
type TeamProposalTeam ¶
TeamProposalTeam is one team the person already has: its id, its name, and the keys of its members among the conversations offered.
type Usage ¶
type Usage struct {
Input int
Output int
CostUSD float64
Duration time.Duration
Turns int
// Calls is EVERY request this session made to a provider — the turn's own
// steps and the auxiliary calls beside them: the namer, the guardian, a
// memory reflex, a look at a picture, a whole child agent folded in.
//
// It is a second counter rather than a wider Turns because Turns has a law
// of its own that other code is written against: it counts steps of the
// CONVERSATION, so a turn that used three tools reads as one turn with
// three steps and the title call that followed it reads as nothing. Calls is
// the honest denominator for "how many requests did this cost me", which is
// a different question and the one a person asking about the bill is asking.
Calls int
// EmptyReflex is how many paid memory-reflex requests returned no answer.
// They remain in every token and cost total above; this count is the reason
// that spend bought no routing or extraction decision.
EmptyReflex int
// CacheRead and CacheWrite are the provider's prompt-cache accounting:
// tokens served from a warm prefix, and tokens written into one. Both are
// zero when the provider says nothing, which is a different fact from a
// cache that missed — but not one a surface can tell apart, so a surface
// shows nothing rather than "0% cached" (design-law-v2 §16 EMPTINESS).
//
// They are read off ai.Usage, which tolerates both spellings the endpoints
// use: Anthropic-native cache_read_input_tokens/cache_creation_input_tokens
// and OpenAI-style prompt_tokens_details.cached_tokens.
CacheRead int
CacheWrite int
// contains filtered or unexported fields
}
Usage is token and cost accounting for one turn or the session total.
func (Usage) CachedShare ¶
CachedShare is the fraction of this session's INPUT that came off a warm prefix, and false when there is nothing to divide.
The denominator is where the two provider dialects have to be reconciled, and they disagree about a fact rather than a name. OpenAI-style endpoints count cached tokens INSIDE prompt_tokens — cached_tokens is a subset, so the total is already Input. Anthropic-native ones count them BESIDE input_tokens — disjoint, so the total is Input + CacheRead. Nothing on the wire says which convention a given row used, so the shape does: cache reads that exceed the input count cannot be a subset of it, and only then are the two added.
Being wrong in the OpenAI direction would report every warm turn as ~50% cached forever; being wrong in the Anthropic direction would report >100%. The test for this is in agent_test.go, one case per dialect.
type UsageCache ¶
type UsageCache struct {
// Path is the ledger this cache is over. Empty means [UsageLedgerPath].
Path string
// contains filtered or unexported fields
}
UsageCache is the ledger read the way home may call it: as often as it likes.
THE PROBLEM IT SOLVES IS NOT THE FIRST READ, IT IS THE THOUSANDTH. Home's clock beats every three seconds, and this file grows by a line on every model call — so a cache keyed on "has the file changed" would find that it HAS, after every single turn, and re-parse a year of spending to learn about one new line. So the cache is keyed on how far it has already read: an unchanged file answers from memory, a GROWN file is read from where the last read stopped, and a file that is not the one it was reading is read again from the beginning.
"NOT THE ONE IT WAS READING" IS AN IDENTITY QUESTION AND NOT A SIZE ONE. A ledger that is rotated away and replaced grows back, and a cache that asked only "is it shorter than what I have parsed" would meet the replacement after it had passed that mark, keep the rows of a file that is gone, and seek into the new one past a prefix it never read — two ledgers added together, with somebody else's morning missing out of the middle. So the cache remembers WHICH file it read (os.SameFile, which is the device and inode the filesystem reports) and how long it was, and it starts over the moment either says this is a different file or a shorter one.
It holds every line it has ever parsed, which is the one thing that makes the tail read possible. That is bounded by [usageCacheLines]: past it the oldest are dropped and the cache says so (UsageCache.Full), because a page drawing a fortnight must not be the reason a long-lived window grows without end.
A zero UsageCache is ready to use. It is NOT safe for concurrent use: it is held by one surface and read on that surface's own goroutine, which is where every reader of it lives.
func (*UsageCache) Full ¶
func (c *UsageCache) Full() bool
Full says the cache has dropped its oldest lines to stay inside its bound, so a total taken from it is a total over what it still holds. A page quoting an all-time figure has to say so; a page drawing a fortnight never has to care.
func (*UsageCache) Read ¶
func (c *UsageCache) Read(since time.Time) ([]UsageLine, error)
Read answers every line at or after `since`, re-reading the file only where it has actually changed.
Errors are answered BESIDE the lines and never instead of them, for [scanUsage]'s reason. A caller that only wants the figures may ignore the error entirely; a caller that wants to say "some of this could not be read" has it.
type UsageGrain ¶
type UsageGrain string
UsageGrain is how coarse a window's buckets are: the `shift+↑↓` axis.
const ( // GrainDay is the ground rung and the one every window starts on. GrainDay UsageGrain = "day" // GrainWeek buckets from MONDAY, because a person's working week starts // there and a sparkline whose bars each straddle two weeks is a sparkline // about nothing. GrainWeek UsageGrain = "week" // GrainMonth is the coarsest rung. There is deliberately no year: a window // of years is a question about a machine older than this program. GrainMonth UsageGrain = "month" )
type UsageLine ¶
type UsageLine struct {
// At is the instant the call was journaled, RFC3339 with nanoseconds.
At time.Time `json:"at"`
// Day is At's LOCAL calendar day, "2006-01-02". It is written down rather
// than derived on read because the reader may be a different process in a
// different zone, and a day that moves depending on who is asking is not a
// day (see [usageDayLayout]).
Day string `json:"day"`
// Model is what answered, and empty where nobody said. It is the id the
// provider knows, not a pretty name: a page that wants "opus 4.1" makes that
// word itself, from one place, the way every other surface does.
Model string `json:"model,omitempty"`
// Role is WHAT the call was for — "title", "guardian", "planner" — on
// every call that went through the role registry and named itself at the
// billing door, and empty on the rest, including every call a turn makes
// for itself. It is internal/roles' vocabulary and NOT the five router
// slots (execution, conversation, verification, naming, planning): nothing
// in the program records which slot a call ran under, and a page that
// labelled this column with those words would be inventing the join.
Role string `json:"role,omitempty"`
// Seat is WHICH TIER'S MODEL ANSWERED, one word of the closed vocabulary
// [Seat] spells — reflex, low, worker, high, mastermind, judge, talk — derived at
// the bank door ([TagUsage], [SeatOfRole], [SeatOfAgent]) and never typed
// at a call site. It is the fact a spend page wants when it asks whether
// the money went on the seat that does the work or the seat that thinks,
// and it rides beside Role: a call the registry seated carries both, and
// the tier's word stays true of a row whose model id is a fallback, a pin
// or a rescue, because the seat names the chair the call ran in, not the
// id that answered.
//
// IT IS omitempty LIKE EVERY ADDITIVE FIELD BEFORE IT, and a row without
// one is a row nobody could seat: every row written before this field
// existed, an errand that resolved its model outside the registry (a media
// pin, a document reader, a tool ask), a receipt that arrived late. Empty
// reads as "nobody said", which is the truth about it.
Seat Seat `json:"seat,omitempty"`
// Calls is how many provider requests this line covers, and it is ONE. The
// field is kept rather than dropped because rows written before issue #269
// carry a whole turn's worth on one line — an observed `calls: 41` — and a
// reader summing a year of this file has to be able to add those honestly.
// Nothing this build writes says anything but 1.
Calls int `json:"calls,omitempty"`
// Input and Output are the tokens. The cache split is deliberately not here:
// this file answers "how much and on what", and the four-way breakdown with
// the cached share in it is in the journal the session id names.
Input int `json:"in,omitempty"`
Output int `json:"out,omitempty"`
// USD is what it cost, and zero means nobody could price it rather than that
// it was free — the same reading [TaskIndexEntry.Cost] has.
USD float64 `json:"usd,omitempty"`
// Reconciled marks a row whose figures came from the provider's own receipt
// after the stream ended without a usage block. It is additive and omitted
// from every ordinary row and every row written before this field existed.
Reconciled bool `json:"reconciled,omitempty"`
// Unbilled marks a missing provider receipt, never a measured zero price.
Unbilled bool `json:"unbilled,omitempty"`
// Empty marks a paid request that returned no answer.
// The role beside it names the reflex, so the row remains useful even to a
// reader that does not know this build's aggregate counters.
Empty bool `json:"empty,omitempty"`
// Session is the 16-hex id of the conversation the call was made in. For a
// piece of work it is the NODE's own journal id and not the conversation
// that asked for it, which is why Task sits beside it: the pair is what
// identifies where the money went.
Session string `json:"session,omitempty"`
// Task is the id of the node this call was made inside, and empty in a
// conversation. It is the node's id within its session, exactly as
// [TaskIndexEntry.ID] is, so the two join.
Task string `json:"task,omitempty"`
// Root is the CONVERSATION the work this call was made inside belongs to,
// and it is empty on a conversation's own line — where Session already names
// it — and on a standing firing no conversation asked for.
//
// IT IS THE ONE FIELD THAT MAKES A FAMILY ADDABLE. Session on a node's line
// is the NODE's journal, which is a file nobody outside the family has heard
// of, and Task is a small integer that restarts with every conversation; so
// with those two alone, "what has this conversation's work cost so far"
// could not be asked of this file at all — it could only be waited for,
// until each node closed and its tally was folded into the conversation's
// own books ([Agent.foldTaskUsage]). It is written on every agent inside a
// family, at every depth and on the check and repair rounds as well, so a
// reader that sums it gets the whole subtree.
//
// A FOLD STILL WRITES NO LINE, so a closed node's calls are counted here
// exactly once — under the node that made them — and never again under the
// conversation they were folded into.
//
// It is ADDITIVE, and absence is ordinary: every line written before this
// field existed decodes without it, which reads as "this is not a family's
// line", and every one of them is a conversation's or a standing item's.
Root string `json:"root,omitempty"`
// Standing is the id of the standing item whose firing made this call, and
// empty everywhere else. A firing is InTask and may also carry a Task id;
// [UsageBySubject] prefers this one, because a person recognises the promise
// they made long before they recognise the run it spawned.
Standing string `json:"standing,omitempty"`
// Workspace is the project root the call was made against — what a page
// groups by, and empty for a conversation held nowhere in particular.
Workspace string `json:"workspace,omitempty"`
// Lane is the machine that answered, spelled exactly as the router spelled
// it — the `provider` field of a streamed chunk. It is the vendor's own
// name and it arrived from the wire; nothing in this build holds a list of
// them.
Lane string `json:"lane,omitempty"`
// TTFTms is the wait before the first token, in milliseconds. It is the
// half of a call's duration a person actually feels: the rest of the answer
// arrives while they are reading.
TTFTms int64 `json:"ttft_ms,omitempty"`
// TPS is output tokens per second over the generation window — the first
// token to the last, and NOT the whole call, because dividing an answer by
// a duration that begins with a queue is how a warm lane behind a long
// prompt gets recorded as a slow one.
TPS float64 `json:"tps,omitempty"`
// Hedged marks a call that was rescued: it went slow, a second request went
// to another lane, and one of the two came back first. It is on the row
// because a hedge is the one thing in this build that can spend money
// twice, and a bill that cannot be told apart from an ordinary one is a
// mechanism nobody can audit.
Hedged bool `json:"hedged,omitempty"`
// HedgeWasteUSD is what the LOSING half of that pair cost. Cancelling a
// stream stops the billing on most lanes and on some it does not, so this
// is zero on a clean rescue and a real figure on a lane that charged for
// the tokens it had already written. It is the number the hedge budget is
// judged on: the mechanism is worth having exactly while this stays small
// beside the seconds it bought.
HedgeWasteUSD float64 `json:"hedge_waste_usd,omitempty"`
}
UsageLine is one model call as the ledger remembers it.
Every field is a fact somebody asked for by name on the spend page, and there is nothing here that is not: which day, which model, what for, how many calls, how many tokens, how much money, and the three ids that say what the money was spent ON — a conversation, a piece of work, a standing promise.
func ReadUsage ¶
ReadUsage reads the ledger, OLDEST FIRST, keeping only lines at or after `since`. A zero `since` keeps everything.
IT READS THE FILE AND NOT THIS PROCESS'S QUEUE, so a row recorded a moment ago may not be here yet (RecordUsage hands it to a background writer). FlushUsage is the door that waits for it, and it is for a process shutting down and for a test — a reader on a beat simply sees the row on its next look.
The order is the file's own and not reversed, because every caller of this is an aggregation over a window rather than a list somebody scrolls: a series wants its days in the order days happen.
IT TOLERATES EVERYTHING A LEDGER CAN BE. A file that is not there is a machine that has spent nothing and answers nil with no error — the first run of a new install must not be an error path. A line that does not parse is skipped, for the task index's reason: two processes appending can in the limit interleave, and one bad line must cost one call's record and not the page. A real failure to OPEN a file that exists is returned, because that a caller can say something about.
func TagUsage ¶ added in v0.3.0
TagUsage writes a row's two name fields, and is the ONE door they go through. Nothing else on the line is touched.
A non-empty role is written to UsageLine.Role; an empty role leaves whatever was there. The seat argument wins when it is one of the seven words; a word outside the set is dropped rather than written, and the seat then falls back to the role's own tier (SeatOfRole); if that fails too, whatever seat the line already carried stays — which is what keeps a caller that knows nothing about a row from overwriting a word somebody else wrote onto it.
type UsageWindow ¶
type UsageWindow struct {
From time.Time
To time.Time
Grain UsageGrain
}
UsageWindow is WHICH stretch of time a spend page is showing and HOW COARSE its buckets are — the two dimensions screen 3d puts on the two arrow axes.
From and To are both INCLUSIVE and both name a BUCKET rather than an instant: they are normalized to the first moment of the bucket they fall in (UsageWindow.Normalized), so a window is a whole number of days, weeks or months and never a fortnight and a half. Every method here answers a normalized window, so a page can hold one in a field and press arrows at it forever without it drifting off the calendar.
The zero UsageWindow covers nothing and is what a page holds before it has decided; LastDays is where an ordinary one comes from.
func LastDays ¶
func LastDays(now time.Time, days int) UsageWindow
LastDays is the window a spend page opens on: the last n days ending today, bucketed by day. It is a constructor rather than a literal at the call site because "the last fourteen days" has an edge case in it — today counts as one of them — and a page that got that wrong would be off by a day forever.
func (UsageWindow) Buckets ¶
func (w UsageWindow) Buckets() int
Buckets is how many bars this window draws — the count that stays the same when the grain changes, which is what makes UsageWindow.Coarser a zoom rather than a jump to somewhere else.
It is bounded by [usageBucketCap]: a window wider than that is a window nobody asked for through the arrows, and counting it out one bucket at a time would be a loop with a person's clock in it.
func (UsageWindow) Coarser ¶
func (w UsageWindow) Coarser() UsageWindow
Coarser is `shift+↑`: one rung up, KEEPING THE BUCKET COUNT — a fortnight of days becomes a fortnight of weeks, which is what screen 3d's own caption says the key does. The window's END is what holds still, because the end is where a person is looking; the start walks back to make room.
A window already on months answers itself. There is no year rung (GrainMonth says why).
func (UsageWindow) Finer ¶
func (w UsageWindow) Finer() UsageWindow
Finer is `shift+↓`, the exact inverse of UsageWindow.Coarser, and a window already on days answers itself.
func (UsageWindow) Holds ¶
func (w UsageWindow) Holds(at time.Time) bool
Holds says whether an instant falls inside this window — the whole of the last bucket included, which is the part a caller gets wrong on its own: `To` names a bucket's FIRST moment, so a window ending today has to hold this afternoon.
func (UsageWindow) Label ¶
func (w UsageWindow) Label() string
Label is the window said out loud — "aug 12 – aug 25" — and it is the control and the reading at once, drawn between the two arrows.
LOWERCASE MONTH, EN DASH, no year while the window sits inside one. That is screen 3d's own spelling, and it is a different question from the one internal/tui3's `sinceAt` answers with "2 Jan": that is an AGE falling back to a date, and this is the edge of a window somebody is steering. A window that straddles new year spells the year on both ends rather than on one, because a range with a year at one end reads as a typo.
A window of one bucket is one date and no dash. A zero window is "" — the emptiness law, so a page that has not chosen a window yet draws no arrows.
func (UsageWindow) Normalized ¶
func (w UsageWindow) Normalized() UsageWindow
Normalized is this window with its grain settled and its ends moved onto bucket boundaries. Every other method answers one, so callers rarely need it; it is exported because a page building a window from a person's own dates does.
func (UsageWindow) Step ¶
func (w UsageWindow) Step(n int) UsageWindow
Step moves the window by its OWN LENGTH — `shift+→` once is the next fortnight, not the next day. Paging is what the arrows on screen 3d do: the label between them is the reading and the control at once, so a press has to change the reading by a whole one of it.
n is how many windows, and it may be negative. A zero window is unmoved, because there is nothing there to move.
type Withdrawal ¶
type Withdrawal struct {
// Reason is that sentence, in the asker's own words.
Reason string `json:"reason"`
// By is who withdrew it, in the same vocabulary [Asker] uses.
By AskerKind `json:"by,omitempty"`
// At is when.
At time.Time `json:"at,omitzero"`
}
Withdrawal is why a question stopped being a question, and who took it back.
A QUESTION IS NEVER SIMPLY GONE. The subject settled, the plan changed, another answer made it moot — whatever it was, the person who saw it on their screen is owed one dim sentence saying so, or the count they were watching drops for no reason they can see.
type WorkState ¶
type WorkState string
WorkState is the little a row needs to draw a worker: it is moving, it is waiting for something (a free hand, an answer), or it has landed.
type World ¶
type World struct {
// Projects are the buckets under the places root, ordered by when somebody
// last spoke in one of their sessions.
Projects []Project
// Artifacts are the deliverables this machine's sessions made, newest first.
// They ride with the world because home draws them under their conversation
// rows, and a surface on another machine cannot read this machine's global
// artifacts index. ReadWorld does not fill them because the places root does
// not say where that index lives; the door that owns the state root does.
Artifacts []Artifact
// Read is when this reading was taken. Every age a surface draws is measured
// from it rather than from time.Now(), so a list drawn from one scan does not
// have rows aging at different instants.
Read time.Time
}
World is every project on this machine, newest first.
func ReadWorld ¶
ReadWorld is ReadHome over a named places root, which is what a test hands a directory it built.
A root that is not there is a machine that has not held a conversation yet and answers an empty world, never an error: there is nothing a caller could do with the news, and drawing nothing is the right screen for it.
func (*World) Adopt ¶
Adopt puts the conversation a window is sitting in into the world when the walk did not find it, and reports whether it had to.
THE WALK CAN BE TOO EARLY FOR THE CONVERSATION IT WAS ASKED FROM. A fresh launch mints a folder and a meta.json with no `lastUserAt`, and [readSessionRow] skips exactly that shape on purpose — an empty shell is not a conversation somebody has had. But a person who opens home FROM that shell is sitting in it, and a screen that listed every conversation on the machine except the one on the terminal behind it would be emptier than the machine actually is. So the surface hands over what it knows — the journal it holds, the title, the workspace, the model — and this fills in whatever the folder can add, under the project the folder belongs to, named by the one rule every other project is named by ([projectName]).
IT INVENTS NOTHING OUTSIDE THE ROOT. A journal that is not a session folder's `transcript.jsonl` two levels under `root` is a memory-only surface or a test fixture standing somewhere else, and the world answers for the root alone. A conversation the walk already found is left exactly as the walk read it.
func (World) Sessions ¶
func (w World) Sessions() []SessionRow
Sessions is every session in the world, flattened, newest first. It is what a search over everything ranks, and what a surface counts to decide whether it has anything at all to draw.
Source Files
¶
- abandon.go
- actioncategory.go
- admission.go
- admission_compile.go
- agent.go
- answers.go
- approvalgate.go
- approvalposture.go
- artifacts.go
- asklane.go
- askwait.go
- assignment.go
- assignment_tool.go
- autonomy.go
- auxiliary.go
- bashbelt.go
- bashbelt_envelope.go
- bashbelt_truncate.go
- bashbelt_worker.go
- beltfacts.go
- blindswap.go
- build_caches.go
- buildnotice.go
- callwindow.go
- cancel.go
- caption.go
- card.go
- chatlog.go
- chatpage.go
- checkmemo.go
- checkpoint.go
- checkpoint_custody.go
- checkpoint_quick.go
- clientdoor.go
- compact_policy.go
- compact_summary.go
- connect.go
- connectcaps.go
- consent.go
- debugrecord.go
- delegate_asked.go
- delegate_door.go
- discarded_usage.go
- effects.go
- effort.go
- estimate.go
- facts.go
- fixblame.go
- fixrecall.go
- fixremedy.go
- fixstore.go
- fork.go
- git_patch_path.go
- git_paths.go
- groundcarry.go
- groundfalls.go
- groundladder.go
- guardian.go
- handlepick.go
- handoff_remainder.go
- handoffcontract.go
- harness.go
- harness_belt.go
- harness_build.go
- harness_task.go
- heldreads.go
- holder.go
- holder_unix.go
- hooks.go
- image.go
- inherit.go
- interactive_budget.go
- interrupt_fan.go
- jobbound.go
- jobfooter.go
- jobname.go
- jobnotice.go
- jobretention.go
- jobretention_unix.go
- jobrow.go
- jobs.go
- jobstop.go
- land_run_tree.go
- landing.go
- lanenews.go
- look.go
- loop.go
- looped.go
- mailbox.go
- media_contract.go
- memory.go
- memory_consolidate.go
- mention.go
- metalock.go
- mp4.go
- newskey.go
- novelty.go
- orchestrate.go
- peek.go
- pending.go
- phasenews.go
- place.go
- placemeta.go
- places.go
- placescontext.go
- plan_capability.go
- plandb_plan.go
- plandb_program.go
- plandb_steer.go
- plandb_tasks.go
- plandb_work.go
- plandigest.go
- presentation.go
- principal.go
- principal_acceptance.go
- principal_audit.go
- principal_delivery.go
- principal_wire.go
- processrule.go
- program_attribution.go
- program_depends.go
- program_outcome.go
- program_wish.go
- programcopy.go
- programcopy_clone_linux.go
- programcopy_unix.go
- programfolder.go
- programhold.go
- promote.go
- prompt.go
- promptprofile.go
- promptprofile_live.go
- question.go
- questionconversation.go
- rail.go
- readhandoff.go
- reasoning.go
- recentplace.go
- recovery.go
- repair_role.go
- replaycursor.go
- resume.go
- retrynews.go
- rewind.go
- route_judge.go
- run_commit_sign.go
- run_contributing.go
- run_tree_changes.go
- runask.go
- runsummary.go
- salvage.go
- seatcompleter.go
- served.go
- session.go
- sessionfile.go
- sidecar.go
- skillattach.go
- skillcatalog.go
- skillturn.go
- spawnfloor.go
- spellout.go
- spendguard.go
- stageearly.go
- standing_contract.go
- standing_isolation.go
- standing_mark.go
- standing_orders.go
- standing_run.go
- standing_world.go
- standingbelt.go
- standingtree.go
- standstill.go
- state.go
- steer.go
- steer_grace.go
- steerquestion.go
- stopcause.go
- stoprun.go
- stopwork.go
- stub.go
- subharness_contract.go
- subharness_env.go
- subharness_intake.go
- subharness_run.go
- sweep.go
- takeover.go
- task.go
- task_audit.go
- task_baseline.go
- task_beat.go
- task_beside.go
- task_branch_protection.go
- task_brief.go
- task_calltrail.go
- task_checks.go
- task_child_run.go
- task_claims.go
- task_continue.go
- task_contract.go
- task_conversation.go
- task_depends_kept.go
- task_divide.go
- task_divide_compose.go
- task_divide_scope.go
- task_divide_sketch.go
- task_divide_wip.go
- task_fork_cleanup.go
- task_forward.go
- task_index.go
- task_job_park.go
- task_land_unsaved.go
- task_landing_question.go
- task_latefold.go
- task_lay.go
- task_ledger.go
- task_live.go
- task_lock.go
- task_merge_round.go
- task_mirror_manners.go
- task_person.go
- task_pressure.go
- task_quick.go
- task_restart.go
- task_result.go
- task_room.go
- task_run.go
- task_run_belt.go
- task_run_clock.go
- task_run_continue.go
- task_run_copy.go
- task_run_index.go
- task_run_money.go
- task_run_recover.go
- task_shape.go
- task_status.go
- task_store.go
- task_tree_mirror.go
- taskclaims.go
- taskcrew.go
- taskcrew_record.go
- taskdelta.go
- taskelsewhere.go
- taskgit.go
- taskgrade.go
- taskground.go
- tasklook.go
- taskmanifest.go
- taskmodel.go
- taskname.go
- taskoutside.go
- taskpreflight.go
- taskpresence.go
- taskrecord.go
- taskstands.go
- taxonomy_boundary.go
- team.go
- team_cap.go
- team_nest.go
- team_questions.go
- team_wake.go
- team_wakewatch.go
- team_wrapup.go
- teamcache.go
- teamevent.go
- teamname.go
- teampropose.go
- teamshape.go
- teamwake_note.go
- title.go
- toolargs.go
- toolask.go
- toolcompact.go
- toolhint.go
- toolhistory.go
- tools.go
- tools_anchor_workspace.go
- tools_ask.go
- tools_capabilities.go
- tools_connect.go
- tools_conversations.go
- tools_doc.go
- tools_editvideo.go
- tools_harness.go
- tools_image.go
- tools_jobs.go
- tools_manual.go
- tools_manual_bound.go
- tools_media.go
- tools_music.go
- tools_pdf.go
- tools_search.go
- tools_sense.go
- tools_settings.go
- tools_skill.go
- tools_speak.go
- tools_standing.go
- tools_subharness.go
- tools_tasks.go
- tools_team.go
- tools_video.go
- tools_view.go
- tools_watch.go
- tools_workspace.go
- tools_write.go
- transcriptread.go
- treehold.go
- turnfold.go
- turnhandoff.go
- turnwall.go
- unattendedrun.go
- usage_ledger.go
- usage_seat.go
- usage_spend.go
- userbash.go
- userupdate.go
- wakecause.go
- wallclock.go
- why.go
- withdrawn.go
- work_tree.go
- world.go
- writeseam.go