exec

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2026 License: Apache-2.0 Imports: 42 Imported by: 0

Documentation

Overview

Package exec runs the leaves of a plan.

A leaf is one unit of work sized for a single agent working alone and in order. This package is the boundary where structure becomes action: above it everything is a graph, below it is a loop with tools.

The Executor interface exists before there is more than one implementation of it, because the interesting question is not how the general loop works but where a specialised one plugs in. A code reviewer, a coding harness, a retrieval-only worker — each is a different way to turn the same Task into the same Outcome, and the graph should not learn which one ran.

Index

Constants

View Source
const (
	ChangeAdded   = "added"
	ChangeChanged = "changed"
	ChangeMoved   = "moved"
	ChangeDeleted = "deleted"
)

The change kinds. They are the vocabulary a reader of the account sees, and they are deliberately the plain English words rather than git's letters: the account is read by a model and by a person, and neither of them should have to be told what "M" means.

View Source
const (
	MeterCost         = "cost"
	MeterDeadline     = "deadline"
	MeterTurns        = "turns"
	MeterNoProgress   = "no-progress"
	MeterToolTimeouts = "tool-timeouts"
)

The bounds the loop writes down, spelled once.

MeterCost and MeterDeadline are LIVE: both are read when the landing reserve is granted and both keep moving while the landing turns run, so what they held at the grant is not what the leaf actually reached. They are re-read at land time. MeterTurns, MeterNoProgress and MeterToolTimeouts are counts of the loop itself and are already final at the moment they are written.

View Source
const (
	CallAI       = "ai"
	CallTool     = "tool"
	CallAsk      = "ask"
	CallRemember = "remember"
	CallRecall   = "recall"
	CallLog      = "log"
)

The six doors, in the spelling a bundle uses. They are constants so that a surface filtering a journal for model calls and the host writing them are reading one string, and a rename is one edit.

View Source
const (
	SelfCloseLostNames = "lost public names"
	SelfCloseUnbound   = "unbound names"
	SelfCloseOwnChecks = "its own checks"
	SelfCloseRegressed = "checks it turned red"
)

The four findings a leaf can raise against ITSELF, spelled once. They are the four Sourced findings the delivery gate raises with no citation to weigh (revision.Regressions, OwnChecksFailing, the removed-public-name settlement, UnboundNames) — the ones that are measurements of the world rather than a judge's reading, which is exactly why a leaf can be handed one and told to fix it without any model being asked whether it is true.

View Source
const (
	FamilyMedia    = "media"
	FamilyDocument = "documents"
)

The optional capability families, and the whole of why they are optional.

Every definition is re-sent on every turn of every leaf. Measured on a six-cell benchmark, the media and document schemas were ~1,112 tokens of each leaf turn's ~3,778-token fixed floor — 18% of every input token the run spent — and not one of those cells could have used a single one of them. A bugfix cannot generate a video. A release note cannot read a PDF that does not exist.

The answer is not to remove the tools; the resident's charter, watch and media journeys genuinely need them. It is to stop paying for them by default. A leaf carries the core loop plus one small tool that says what else exists; asking for a family arms it for the next turn and every turn after. The judgement of whether the work needs a camera stays with the model doing the work, made at the moment it knows — not predicted for it at compile time by a call that has never seen the workspace.

The cost is honest and worth naming: a job that does need media pays one extra turn and one prefix-cache invalidation at the moment it arms. A job that does not — the overwhelming majority — pays nothing at all, ever.

View Source
const AllowProviderKeysInShell = "CODEAF_ALLOW_PROVIDER_KEYS_IN_SHELL"

AllowProviderKeysInShell is the opt-in that keeps a provider credential in a model's shell. It is read from codeaf's own environment through env.Get, so it carries the same retired-prefix fallback every owned variable does, and it is never read from a model's command, so only whoever started codeaf can grant it — a task that genuinely needs the running key (a script that calls the provider's API itself, say) gets it by exporting this before codeaf starts, not by asking the model to.

View Source
const AttributionAssistedBy = "Assisted-by: CodeAF"

AttributionAssistedBy is the line above the co-author, and on its own it is the whole of that line: `Assisted-by: CodeAF`. It names the model that wrote the commit only through AssistedBy, which adds ` (<model>)` when there is a model to name, so that `git interpret-trailers` can answer who typed it beyond the account while the co-author stays last, the order GitHub reads.

THERE IS NO EMPTY `()`. A path that does not know its model, and a person who turned the model's name off (internal/config's `attribution.model` row), both get the bare line, which is still true; a pair of empty brackets would be a line that looks like it lost something.

View Source
const AttributionAssistedBySlot = "{assisted-by}"

AttributionAssistedBySlot is where AttributionLaw holds the `Assisted-by` line until a surface that knows its model fills it (FillAttribution). It is spelled so that a page which forgot to fill it reads as broken to anybody who looks, rather than as a plausible line crediting nobody.

View Source
const AttributionCommentFooter = "" /* 132-byte string literal not displayed */

AttributionCommentFooter is the mark on a COMMENT — an issue comment, a pull request comment, a review comment — and it is the quietest of the three on purpose.

A comment is a remark in somebody else's conversation. A body is a document with a foot, and a commit has a trailer block, so a line at the end of either is a line in a place a reader's eye already skips to; a comment has no foot, and the em-dash rule that opens the body footer would put a horizontal break through the middle of a thread. So this is ONE LINE, no separator, lowercase, and wrapped in `<sub>` — which GitHub renders at about 85% size in a muted weight everywhere a comment is rendered. It says who drafted it and stops: no "reviewed and owned by the author", because a comment nobody signed off is not a deliverable somebody owns, and the sentence would be doing work the person did not ask for.

AT MOST ONCE PER THREAD, which is the part that keeps it from becoming advertising. The first comment codeaf leaves in a thread carries the line and every later one carries nothing: the reader has been told, and telling them again on the fourth reply is the behaviour that makes people turn a setting off. [attributionPrompt] and the chat's belt fact both state that bound, and the three cases it is never right for at all — a one-line reply, anything inside a code or suggestion block, and words the person dictated, which are theirs and not codeaf's to sign.

View Source
const AttributionIssueFooter = "" /* 155-byte string literal not displayed */

AttributionIssueFooter is the same line for an issue; only the medium differs.

View Source
const AttributionLaw = "SIGN GIT WORK YOU DO WITH `bash`, GENTLY AND ONCE. A commit ends with a blank " +
	"line, then `" + AttributionAssistedBySlot + "` and `" + AttributionTrailer + "` as its last two lines. " +
	"A pull request or issue body ends with " + AttributionSeparator +
	" alone on a line and then `" + AttributionPullFooter + "`, `utm_medium=issue` on an issue. " +
	"A comment ends with `" + AttributionCommentFooter + "` on its own last line, ONCE per thread — never on a " +
	"one-liner, in a code or suggestion block, or on words they dictated. " +
	"Nowhere else: not in code, a commit subject, a README, a deliverable or your reply. " +
	"A CONTRIBUTING policy banning AI trailers wins: leave them out and say so."

AttributionLaw IS THE ONE WORDING, AND IT IS ONE BECAUSE TWO SURFACES SAY IT. The resident's leaf loop appends it to its standing contract ([attributionPrompt]) and the v3 chat renders it as a belt fact beside the tool that does the committing (internal/session's beltfacts.go). Two paragraphs written separately would drift into two different laws about the same four bytes, and a model told one of them in a task and the other in the conversation is a model deciding which to believe.

It is composed from the constants above rather than quoting them, for the reason they are constants: the exact bytes are the feature, and a paragraph that retyped the trailer would be the one copy nobody re-read.

THE COMMIT SENTENCE SPELLS BOTH TRAILER LINES, and the `Assisted-by` one is a slot (AttributionAssistedBySlot) because only the surface knows which model it is running: FillAttribution puts AssistedBy's line there. One sentence carrying both lines in their order is what keeps every commit the model writes shaped like the ones the harness writes itself — the same two lines after one blank line, and nothing else.

The issue footer is named by the ONE PARAMETER THAT DIFFERS rather than spelled a second time. Everything ahead of that parameter is byte-identical to the pull footer, and this sentence rides in front of every request the chat makes: a second URL here is 145 bytes bought on every tool round of every turn, forever, to say what the substitution already says. The comment line IS spelled out, because it is not the same line with a parameter changed — different case, different wrapper, no owning clause — and describing it would cost more than the constant does.

It is ONE PARAGRAPH so that the chat can carry it as a single belt bullet beside the tools it names, which is the register that section is written in. Three of its sentences are the three places, one each, and the fourth is the whole of where it may never go. MOST OF ITS BYTES ARE THE CONSTANTS THEMSELVES, which is the floor: a footer the model half-remembers is a footer that attributes nobody and counts as nothing, so this is the one law on the belt that cannot be paraphrased down.

IT HAS NO OFF. Signing used to be a settings row; since 2026-09-23 it is always on, and the only thing a person may turn off is the model's name in the `Assisted-by` line. What still wins is a repository's own CONTRIBUTING policy against AI trailers, which is the repository's rule and not a person's setting, and the law's last sentence says so.

View Source
const AttributionPullFooter = "" /* 162-byte string literal not displayed */

AttributionPullFooter is the one footer line on a pull request codeaf opens.

View Source
const AttributionSeparator = "—"

AttributionSeparator is the em-dash line that opens the body footer.

View Source
const AttributionTrailer = "Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com>"

AttributionTrailer is the commit trailer, and the only place codeaf may sign a commit it wrote for the user. The address is ID-prefixed — `267109073+agentfield-bot` is the account's numeric id — because that is the form GitHub links to the CodeAF account and renders the co-author with its avatar.

View Source
const DefaultLeafTokens = 220_000

DefaultLeafTokens is the budget one leaf may spend, and it is calibrated rather than picked.

WHAT IT COUNTS IS WHAT THE JOB PAYS. spent() reads uncached prompt at full rate, cache reads at cachedTokenWeightPercent — the provider's own discount, not a weight this file invented — and the completion. So the grant is a bill expressed in token-equivalents, and a leaf that spends it has cost the job what the job agreed to spend on one leaf, whether it did that in twelve expensive turns or sixty cheap ones. That is deliberate: the alternative is a meter that charges a leaf for re-reading its own transcript, which is a fact about how conversations work rather than about what this leaf did.

AND IT IS NOW THE ONLY BOUND THAT MAY LAND A LEAF ON ACCOUNT OF ITS WORK. Two cumulative prompt ceilings used to be able to as well; both were Σ over turns of the prompt, which is turns × mean-context wearing a token name, and on ink s9 of 2026-08-29 one of them cut three leaves at turns 13, 12 and 9 with a third of this grant unspent. They are wrap-up pressure now. The other two things that may still stop a leaf measure something else entirely — maxTurnBackstop counts iterations, and the no-progress guard reads whether it is working at all. See PERF.md, "A leaf's bounds", and meter.go's reuseCeiling for the arithmetic.

IT IS ONE NUMBER AND EVERY DOOR READS IT FROM HERE. `codeaf exec`, `codeaf run` and the chat surface each spelled 150_000 of their own, and chat's constant carried a comment promising it "mirrors the headless run defaults exactly" — an intention where the repository's one-source-of-truth law wants an interpolation. A promise in a comment is how two numbers drift.

IT IS 220,000 BECAUSE THAT IS WHAT REAL REPOSITORY WORK MEASURED, AND BECAUSE TWO INVARIANTS IN THIS PACKAGE BOUND IT FROM ABOVE. Both halves are measurements, and the next person to reach for this number needs the second half as much as the first.

The measurement against real work: issue #898 — the frame law widened to see a package function taking the surface as a parameter — run headless on z-ai/glm-5.3 on 2026-09-11, run 04c2404b26072e41. Neither half of the original calibration above held. The median call carried 14.2k prompt tokens, not 11k, and the leaves that did the work ran 14 to 41 turns, not 8 to 16. At 150,000 every one of them was landed mid-edit:

leaf                        turns   spent    grant then
Issue 898 frame law fix        14   184,411   150,000
finish-issue-898               41   190,524   164,462
Answer remaining offenders     14   268,971   211,852

(A grant above this constant is this constant plus the dependency term gatheringGrant adds — it is cmd/codeaf's, in subharness.go, which this package cannot link to; the overshoot past each is the landing reserve doing its job at landingTokenShare of the grant.) The run then re-planned around every landing — five rounds and seven nodes for one issue, 43 minutes, $2.11, and a delivery gate that refused at the end for want of time — while the WORK itself was correct and committed after the first two leaves. Every split is paid for twice: once in a fresh planning round, and once in the context the next leaf has to be told again. 220,000 covers the first two leaves outright with room over. The third, which had already been lifted to 211,852 by its dependency term and spent 268,971, is a job that wants splitting and is not an argument for a larger number.

THE CEILING, AND WHY IT IS NOT 250,000. Two invariants bound this from above and neither may be moved to let a figure through. The band is 200,000 to about 241,000, measured by sweeping the constant and running them:

  • TestACacheDiscountedRunawayLandsOnItsMoney pins that a 98%-cached runaway runs out of money before the 1.4M raw tokens the audited melt-downs reached. At the discount the raw count is about 5.8× the grant, so 242,000 is where it arrives and 250,000 pushed 1,442,112 — exactly what the grant forestalls. 220,000 reaches about 1.28M.
  • The same test closes by asserting that cost alone would have bought the leaf several times the work it did, and at 190,000 the simulated cost bound and the real run both land at 85 turns, so the comparison stops separating and the test says so. That is the floor, and it is why this is not 190,000 even though the work only demanded that much.

THE THIRD BOUND WAS NEVER REAL AND IS NOW CUT. TestObservationWindowIsSizedFromContextNotSpend appeared to pin the grant under 196,608, because it compared the context-derived window against DefaultLeafTokens/6. But the window has not read this constant since observationWindow became ctxbudget.ObservationBytes of the model's OWN context; the division was the test recomputing a historical figure — what the spend ceiling used to buy — from a number that had since moved. So it failed whenever the grant ROSE, which is backwards, since a larger grant does not shrink the memory a leaf is given. That constant is frozen at the 25,000 it actually was, and the assertion it supports is unweakened.

AND THE ORIGINAL LESSON STILL STANDS, because three comments in this file rest on it. It was set at 400k once, which permitted around 37 turns, and every leaf ran to exactly that — the loop has no intrinsic reason to stop, so the binding limit is not a safety net, it is what makes the loop converge. Set it loosely and the model will spend all of it. maxTurnBackstop's own calibration and landingTokenShare's sizing both cite this paragraph; the figures they cite are the ORIGINAL calibration above, and the measurement in this comment is the reason both of them want re-reading when the grant moves.

So this number is not a knob. It is load-bearing: it sits inside a band 41,000 wide, with a measurement under it and two invariants over it, and moving it again means re-running the sweep in #920's replication and rewriting this comment, PERF.md and the manual page that quotes it. A lane that needs a bigger grant for one run passes `--token-budget` to `codeaf exec` or `codeaf run` rather than editing this.

View Source
const DeoptHeldWord = "needed a closer look, and the long way would reach further than this program was allowed to — so nothing else was tried"

DeoptHeldWord is what a person reads when the long way would go further than the program they said yes to was allowed to go, so it was not taken. It is a constant for DeoptWord's reason, and it obeys the same vocabulary law: nothing here calls this a failure, a refusal or a policy — the step needed a closer look, the long way would reach past what was agreed, and the work stops where it is so somebody can say what they want done.

View Source
const DeoptWord = "needed a closer look — handled it the long way"

DeoptWord is the person-facing account of a run that was handled the long way. It is a constant because a surface drawing it and a journal recording it have to be quoting one sentence.

View Source
const FoldTurns = 2

FoldTurns is the whole run of a node whose material is already in its prompt.

Two, and the second is a recovery rather than a continuation. A fold is a read-and-write over material it was handed, so one call is the honest shape of it: the measured alternative is a join that read three files it had already been given and spent thirteen turns and 181,354 tokens doing it, against about 7,700 for the single pass — 23×, and 160 of the 366 seconds of that job's critical path.

One turn alone would be a trap, though, and the trap is not the model's fault. A first call can come back asking for a tool — to write the file it was told to produce, most often — and a first call can come back empty or truncated, which the loop already refuses to accept as a deliverable. Either is a turn spent without an answer, and a cap of one would settle the node on it. So the second call exists for exactly those two endings, and after it the loop stops: a fold still asking for tools on its third breath is not folding, and what happens to it is what happens to any leaf that reaches its cap, which the retry and judging paths above this loop already know how to handle.

View Source
const GovernorInFlightCeiling = 64

GovernorInFlightCeiling is a backstop, not a scheduler. It exists so a pathological fan-out — a graph that goes a thousand leaves wide, a runaway splice — cannot exhaust file handles or goroutine budget. It is deliberately far above any width a real plan produces, so that in ordinary operation it decides nothing at all: the shape of the graph is what is supposed to decide how much runs at once. If this number is ever the thing a run is waiting on, the interesting bug is upstream of here.

View Source
const LinearSubharness = plan.LinearSubharness

LinearSubharness is the worker. An empty name resolves to it, but the two are not the same fact: empty is a question nobody answered, and this name is the answer said out loud. GeneralistSubharness is the predicate that keeps them apart, and old graphs are full of both.

View Source
const (
	// MaxAttachmentBytes is the ceiling on one attached file.
	MaxAttachmentBytes = 25 << 20
)

An attachment used to be a reference to somebody's own filesystem. The job read the bytes when it ran, which meant a PDF moved into a different folder on Tuesday broke a retry of Monday's work, and a follow-up the next morning carried nothing at all — the durable record named a file, and the file was the person's, not ours.

So an attachment is copied when it is mentioned. The bytes go into the content-addressed store the moment the message is sent, the message carries a reference to them, and every later reader — a retry, a follow-up in a new session, a continuation a week later — reads our copy. The person's file is opened once, read-only, and never touched again.

View Source
const ModeFold = "fold"

ModeFold is what Outcome.Mode says when the loop ran a task as a fold.

View Source
const NoWall = time.Duration(-1)

SelfCloseRoom is what the leaf has left of ITS OWN meter, which is the whole of what a close is allowed to spend.

There is no constant here and there must never be one. A close that had its own grant would be a second budget nobody voted for, riding on top of the one the scheduler leased this leaf; what the leaf did not spend is already its, and spending it on making its own work correct is the cheapest thing it can be spent on. NoWall is what a belt with no clock at all passes for the wall. It is not a duration and it is never subtracted from: it says the question does not apply here, which is a different answer from "none left".

Variables

View Source
var ErrNoSubharness = errors.New("no subharness by that name")

ErrNoSubharness is what a lookup answers for a name nothing in this build or any of its stores has heard of. It is a real error and not a fallback: a LEAF that names an unknown worker is served by the generalist (Registry.For's promise, unchanged below), but a PERSON who typed a name into `/subharness` has made a typo and would be badly served by silently getting something else.

View Source
var ErrNotWired = errors.New("nothing is wired behind this door yet")

ErrNotWired is what every door of UnwiredEnv answers. It is a typed error so a runner can tell "this build has nothing behind that door" apart from "the call was made and failed", which are opposite facts about whose problem it is.

Functions

func AccountFor

func AccountFor(
	ctx context.Context, workspace *Workspace, task Task,
	base string, secondReading bool, outcome *Outcome,
)

AccountFor fills in the leaf's account of its own work, where there is anything to account for.

It is called from exactly one place — the end of PhotographAfter's body, through a defer, so that it runs on every one of that function's exits and no belt has to remember it. That is the whole structural point of the repair and account_writer_test.go is what keeps it true: a measurement wired into one belt is a measurement that leaves with that belt.

base is where the repository's history stood when the tree was photographed, carried from PhotographBefore on the Opening. It cannot be re-derived here — by the time a leaf lands, HEAD is wherever the leaf left it — and it is empty for every root that is not a git work tree, which is the honest "no claim" and never "measured, and nothing moved".

secondReading is the seam's own answer to whether THIS leaf's reading of the finished tree was taken, and it is the retake law read a second time rather than a fact of its own. It decides the checks and nothing else: the files, the span and the patch are what the tree says, and the tree says it whether or not a suite ran.

A RUN WITH NOTHING TO ACCOUNT FOR LEAVES THE ACCOUNT NIL. A leaf that changed no file and took no reading has made no claim, and nil is how Account spells that everywhere it is rendered; an empty account attached anyway would turn "nobody looked" into "we looked and there was nothing", which is the one substitution the emptiness law exists to forbid.

func AlsoWithLiveness

func AlsoWithLiveness(ctx context.Context, mark LivenessMark) context.Context

AlsoWithLiveness arms a second listener without displacing the first.

TWO LISTENERS ARE THE ORDINARY CASE, not an edge one. The claim reaper listens because it is deciding whether a node is held by nobody; the node watchdog listens because it is deciding whether to give up on this particular worker. They are different questions asked by different code at different levels, about the same fact, and the fact is reported once. A plain WithLiveness at the second site would silently take the mark away from the first — which is the whole class of defect the transcript sink had when two recorders were armed on one attempt.

func AssistedBy

func AssistedBy(model string) string

AssistedBy is the `Assisted-by` line for a commit written by this model: the model's bare name in brackets (BareModelName), or the bare line when there is no name to give. It is the ONE place that line is spelled with a model in it, and every writer of the line comes through here — the chat's belt fact, the leaf loop's contract and the harness's own landing commits — so the three cannot drift into three spellings of one model.

func AttributionTrailers

func AttributionTrailers(model string) string

AttributionTrailers is the whole trailer block codeaf ends a commit with: the `Assisted-by` line and then the co-author, two lines, in that order, and nothing else.

func BareModelName

func BareModelName(id string) string

BareModelName is a model id as the `Assisted-by` line names it: the model and nothing about who served it or how.

Two things come off, and nothing else does:

the provider or company  everything up to the last `/`, and OpenRouter's
                         leading `~` alias marker with it:
                         `deepseek/deepseek-v4-flash` → `deepseek-v4-flash`
a routing suffix         a trailing `:free`, `:nitro` and their kind, which
                         say how the request was routed or how hard to
                         think, never which model answered

THE MODEL'S OWN VERSION OR DATE STAYS, and that is the difference between this and the word a status line shows (internal/tui2/modelui's ModelWord, which drops a release date to save cells): a trailer is provenance, and `deepseek-v4-flash-0731` and `deepseek-v4-flash` are two different models to anybody reading the history later.

THE SUFFIX LIST IS CLOSED ([routingSuffixes]), for the reason internal/lane closes its own: an open rule would read a local model's size tag — the `:32b` of `qwen3:32b` — as routing and strip the one part of the name that says which weights ran. A suffix this build has not been taught is kept.

func ContributingRefusesTrailers

func ContributingRefusesTrailers(text string) bool

ContributingRefusesTrailers conservatively reads a repository's own ban on AI attribution. A requirement to add or keep a trailer wins over negative wording such as "do not submit code without one" or "do not remove it" in the same sentence: neither asks the harness to omit attribution.

func DeoptHeld

func DeoptHeld(manifest Manifest) bool

DeoptHeld reports that this program's ceiling does not reach the long way, so the long way must not be taken.

THE CEILING A PERSON APPROVED SURVIVES THE FALLBACK, AND THIS IS THE LINE THAT MAKES IT TRUE. Manifest.Whitelist is what the approval card showed somebody this program may reach — "can use · nothing" for the empty one (internal/subharness's cardFoot) — and every call the program makes during its run is checked against it. The generalist underneath the fallback has no such ceiling and cannot be given one: its five tools are unconditional and sit in a fixed order by law (Toolbox.Definitions states why the order is load-bearing and why the five never move), so there is no seam to filter them through.

Without this, a subharness somebody approved to touch nothing became an unrestricted shell agent in their workspace the moment a guard did not pass — silently, with no second question, and with nothing on any surface saying the ceiling had come off. NEVER SILENTLY WIDEN WHAT SOMEBODY AGREED TO. Where the ceiling does not reach a shell the honest answer is that the work stops incomplete and says so, and the person decides what happens next.

func DeoptHeldLine

func DeoptHeldLine(because string) string

DeoptHeldLine is DeoptHeldWord with the program's own reason after it, in DeoptLine's shape and for its reason.

func DeoptLine

func DeoptLine(because string) string

DeoptLine is DeoptWord with the program's own reason after it, where the program gave one. A guard's Because line is written for a person to read, so it is carried through rather than summarized; a run that said nothing extra draws the bare word, never an empty colon (the emptiness law).

func DeoptLineFor

func DeoptLineFor(manifest Manifest, because string) string

DeoptLineFor is DeoptWordFor with the program's own reason after it.

func DeoptWordFor

func DeoptWordFor(manifest Manifest) string

DeoptWordFor and DeoptLineFor are which of the two sentences this program's fallback is going to produce, asked BEFORE it runs. A surface announces what is about to happen, and one that said "handled it the long way" over a run that was about to stop would be telling somebody the opposite of the truth.

func FellBack

func FellBack(result RunResult) bool

FellBack reports that this result is asking to be handled the long way. It is asked in one place so that no surface invents a second reading of it, and it is the same shape RunResult.Finished is: a question about the result rather than a field every caller re-tests.

func FillAttribution

func FillAttribution(text, model string) string

FillAttribution puts the `Assisted-by` line for this model (AssistedBy) into every slot the text holds. An empty model fills the bare line, never an empty `()`.

func FlushTranscript

func FlushTranscript(ctx context.Context)

FlushTranscript makes this context's transcript durable, if it has one. It is what a runner calls on every way out of a leaf — the ordinary return, the watchdog, the recovered panic — so that whatever the worker had already done survives the way it ended.

func GeneralistSubharness

func GeneralistSubharness(name string) bool

GeneralistSubharness reports whether a name is the generalist, named. It is deliberately not KnownSubharness: this one answers no to the empty string, which is the difference between "the worker, said out loud" and "nobody said anything" — two different things to every reader that would otherwise fill a blank in from somewhere else.

It is the registry's answer rather than a comparison each surface spells for itself, which would be one rename away from being wrong.

func JobShellEnv added in v0.3.0

func JobShellEnv(environment []string) []string

JobShellEnv is the environment every shell a model's command runs in must receive: with TMUX and TMUX_PANE removed, TMUX_TMPDIR pointed at a socket directory codeaf owns, and every provider credential codeaf itself knows about removed unless AllowProviderKeysInShell says otherwise.

A bash call the model runs inherits this process's environment, TMUX and TMUX_PANE included, so a bare `tmux` it runs targets the very server hosting the chat. That is how `tmux kill-server` once took down the chat that ran it (issue #576), and on a shared socket it reached every run on the box. A person running codeaf inside tmux has the same exposure.

THE FLOOR IS BOTH HALVES, NOT EITHER. Unsetting TMUX/TMUX_PANE alone lets a bare `tmux` land on the user's default socket — the host server is safe, but the user's own tmux is still reachable, and a job that meant to reach its own server cannot. A private TMUX_TMPDIR alone leaves the inherited TMUX/TMUX_PANE pointing straight at the host server. Together they name a namespace a job's `tmux` reaches, and nothing outside it. A test that needs its own tmux still works: it gets that private TMUX_TMPDIR rather than a stripped-to-broken env.

A PROVIDER KEY EXPORTED FOR CODEAF IS NOT A KEY HANDED TO THE MODEL. A person who exported OPENROUTER_API_KEY so codeaf could talk to a provider did not thereby mean every shell command the model runs should be able to read it back and print it (issue #1484). A key that lives only in the profile file never reaches os.Environ() in the first place — config.APIKeyAt reads it without exporting it — so stripping the environment is the whole fix; there is nothing there to leave behind.

A NIL SLICE IS THE PARENT'S ENVIRONMENT. runShell and the background-job registry leave cmd.Env unset on the benchmarked bare path, which inherits everything; the caller must now hand a real, stripped environment, so a nil here is read as os.Environ() and stripped the same way.

func KeepAttachment

func KeepAttachment(root, path string) (string, error)

KeepAttachment copies one file the person explicitly attached into the store at root and returns the durable reference to it. Only the named file is read: nothing here walks a directory, follows a listing, or reaches for a sibling.

func KnownSubharness

func KnownSubharness(name string) bool

KnownSubharness reports whether this build can run a name as written. There is one worker, so the answer is yes for the generalist — named, or left blank by a plan that never asked — and no for everything else.

It has one reader left, and it is about old graphs: a node stored by a build that had a second worker still names it, and this is how a surface tells that the name it is holding is not a worker this build has. Such a node runs linear and says so once.

func LeafShape

func LeafShape(node *plan.Node) string

LeafShape exposes the scheduler's population key to resident execution. Both surfaces must write observations into the same ledger cells or neither has enough evidence to learn a useful ordering.

func MediaSlug

func MediaSlug(prompt string) string

MediaSlug is exported for deterministic filename tests and integrations that want to preview where an artifact will land.

func Mutates

func Mutates(executor Executor) bool

Mutates asks the question of any executor, including the ones that have never heard of it. Nil and non-mutating both answer false.

func PhotographAfter

func PhotographAfter(
	ctx context.Context, workspace *Workspace, history *store.Store,
	wall time.Duration, task Task, opening Opening, changed bool, outcome *Outcome,
)

PhotographAfter takes the second reading and writes what the two readings say onto the outcome.

changed is the workspace's own account of whether THIS leaf moved anything. The opening's own Moved says the JOB had already moved the tree before this leaf's reading was taken (verify.TreeState, by way of PhotographBefore). Either is reason enough to take the second reading: a continuation that only rewrote its account still hands over a tree an earlier round may have broken, and the whole reason the baseline is the job's is so that breakage is still visible here. Neither is the case for a leaf that changed nothing in a tree nothing had changed — it cannot have regressed anything, and the reading it is holding is a reading of the very bytes in front of it, so it stands rather than being taken again for an eighth of the wall.

It belongs at whatever single point a belt lands through, and it runs on an EXHAUSTED landing exactly as on a chosen one: a leaf ordered to stop still changed the tree it was standing in, and a reading nobody took is the silence this whole file exists to end.

Being that one point, it is also where the leaf's own account of its change is taken — see AccountFor, which it defers so that no exit from here can skip it.

func ProcessIdentityMatches

func ProcessIdentityMatches(pid int, startedAt time.Time) (bool, error)

ProcessIdentityMatches protects re-adoption from PID reuse. A platform that cannot expose start time reports an error; callers can preserve the process without pretending identity was proven.

func ProcessStartTime

func ProcessStartTime(pid int) (time.Time, error)

ProcessStartTime is the platform identity helper used both when a service is recorded and when it is re-adopted. Darwin and the other Unix targets supported by codeaf expose lstart through ps; callers degrade safely when a platform does not.

func RanOutOfRoom

func RanOutOfRoom(err error) (time.Duration, bool)

RanOutOfRoom reports that an error is a worker that was still working when the clock stopped it, and how long it was given.

It exists so that "the ending was the clock" is asked of the TYPE rather than of the sentence, at every level that has to decide what happens next. The one that matters is the node's: an ending that is exhaustion is not a failure, so the node goes back on the queue with its record intact rather than being settled failed with a run's worth of work in it (see resident.Runner.runOne).

func RanOutSubject

func RanOutSubject(stop StopReason) string

RanOutSubject names WHAT A LEAF RAN OUT OF, in the words a person would use rather than in the loop's own one-word ending.

It lives beside StopReason.OutOfRoom and for the same reason: the answer travels. The record and the headless stream say it at the moment the leaf stops (cmd/codeaf's exhaustionWords), and the scheduler says it again on the other side of the seam when it hands the node back to the queue (resident.outOfRoomClaimReason) — and for as long as each of them kept its own table, one sentence could name the bound that fired while the other named only the meter that measured it, about the same leaf, three lines apart.

An ending nobody recorded, and any ending that is not a leaf running out, gets the neutral phrase: a reason a reader cannot name is worse than a general one, and this is only ever composed for a leaf that was still working.

func Record

func Record(journal Journal, entry JournalEntry) error

Record writes one entry through a journal that may not be there. It is the door every host call uses, so "journaled before it returns" is one line at each call site and cannot be forgotten differently in six places.

func Requeued

func Requeued(err error, record func() int) (allowed time.Duration, recorded int, requeue bool)

Requeued is the whole rule an exhausted node is judged by, and it is asked by both schedulers: the resident's, which drives `codeaf do` and the chat, and the one-shot Scheduler that drives `codeaf run`.

AN ENDING THAT IS EXHAUSTION IS NOT A VERDICT ON THE WORK. The worker ran out of the room it was given; that is the queue's input and the growth governor's, so the node is offered again and the next claim carries on from the record this one left. The ink run of 2026-08-29 journaled exactly that sentence and then settled the node failed in the same second, over a twenty-six kilobyte patch and seventy-three minutes of unspent wall — because the sentence was in one place and the decision was in another.

IT IS GATED ON THERE BEING SOMETHING TO RESUME FROM, which is what keeps it from being an unbounded retry: a claim that reads an empty record is the same cold start again, and an attempt that recorded not one turn before the clock stopped it has told us the only thing it is going to.

The record is read through a function rather than passed in because reading it costs a query on the resident's side, and the question is only reached for an ending that is exhaustion in the first place — a run whose leaves fail for ordinary reasons must not pay for a record it will never consult. What comes back is the room the worker was given and how much of its work survived, which is what the release sentence and the stream line are both composed from.

func SelfCloseKinds

func SelfCloseKinds(found []SelfCloseFinding) []string

Kinds names what a set of findings is, for a caller that has to say it out loud — the trace line inside the leaf, and the stream line outside it.

func Settle

func Settle(node *plan.Node, outcome *Outcome, err error, journal func())

Settle writes one leaf's measured ending onto its plan node and hands the document that ending changed to the journal.

It is one function because both doors owe the same nine fields and the same state rule, while only the chat door has a durable namespace to journal. A new door reaches this seam rather than growing a partial copy of the ending. For the headless door this also carries Calibration into the profile record cmd/codeaf/run.go builds, where the worker's account of its own fit was previously absent.

The journal is called with the ending settled and before a door's own last word about the node, which costs nothing that can be lost: a rehydrated plan re-derives State, Result and Failure from the durable rows (cmd/codeaf/chat.go, syncPlanState), and the fields only the journal can carry are exactly the ones written here. journal is nil where a surface has nowhere durable to write.

func SignCommitMessage

func SignCommitMessage(message, model string) string

SignCommitMessage is a commit message as codeaf leaves it: the message with its trailing newlines taken off, ONE blank line, and the trailer block.

A blank line and the lines after it is what a trailer block IS, in every version of git there has ever been, which is why this appends rather than handing the lines to `git commit --trailer`: that flag arrived in git 2.32, and a person on an older git would get a commit that silently carried no attribution at all.

func SignCommitMessageOnce

func SignCommitMessageOnce(message, model string) string

SignCommitMessageOnce keeps a worker's own attribution when it already has both lines. A partly signed message gains just the missing line, without moving or duplicating the line the worker wrote.

func StartDetachedService

func StartDetachedService(command, dir, logPath string) (int, time.Time, error)

StartDetachedService uses the same shell, log, session/process-group shape as background jobs. No pipe or resident goroutine is created.

func StopServiceProcess

func StopServiceProcess(pid int, startedAt time.Time) error

StopServiceProcess terminates the whole detached session, preserving the job registry's TERM-then-KILL contract without requiring its waiter channel.

IT REFUSES A RECYCLED PID. A service is addressed by a pid recorded at start; under pid pressure that number can name somebody else by the time a stop runs, and signalling it would tear down an unrelated process group. So the recorded start time is checked against the live process first, and a process that is no longer the one that was started — or whose identity cannot be read — is left alone. A missed stop leaks one service; a wrong stop destroys someone else's work.

func SubharnessChosen

func SubharnessChosen(name string) bool

SubharnessChosen reports whether a node's worker column was ever written at all. Only the empty string is nothing.

func SuggestPath

func SuggestPath(nodeID int, title string) string

SuggestPath derives a distinct output path for a node from a numeric id that is unique within one graph — which is what a plan node's id is.

It is not what every caller has. A store node's creation sequence is the splice's, shared by every sibling it created, and passing that here is how five parallel briefs on five different topics landed on one filename. Any caller whose identity is not a per-node number wants SuggestPathFor.

func SuggestPathFor

func SuggestPathFor(key, title string) string

SuggestPathFor derives a distinct output path from an identity that is unique per node and a title that is only there to be read.

Uniqueness has to come from the key alone. The title cannot carry it: titles are clipped for display — a spliced job's to 48 characters — and five parts of one ask share their opening words, so five distinct topics arrive here as one identical string. Siblings run concurrently by construction, so a shared name is not a warning in a log — it is four deliverables silently overwritten by the fifth.

func TraceFile

func TraceFile(home, leaf string) string

TraceFile is where a node's recorder is written, under the directory the harness keeps its own files in for that job. Writers use this and only this.

func TracePath

func TracePath(home, leaf string) string

TracePath is where a node's recorder can be read from: the current location, falling back to the pre-move .obs spelling when only that file exists.

The fallback is what keeps a finished run readable after the move. A trace is written once and read for as long as anyone is still asking what a node did, and a relocation that silently emptied every existing run's view would be a worse defect than the contamination it was fixing. When neither file exists the current path is returned, so an error names where the recorder should have been rather than where it used to be.

func WatchdogAbove

func WatchdogAbove(deadline time.Duration) time.Duration

WatchdogAbove is the node watchdog over a deadline that has already been decided — a retry running on the shape its first attempt was given.

It is derived here rather than at each dispatch site for the reason stated on SubharnessInfo.Deadline: `deadline + 2*time.Minute`, written out by hand in seven places across three files, is a pad that disagrees with itself the first time one of them is edited.

func WithLiveness

func WithLiveness(ctx context.Context, mark LivenessMark) context.Context

WithLiveness arms one attempt's liveness reporting. Like the transcript sink, it belongs to the attempt rather than to the worker.

func WithTranscript

func WithTranscript(ctx context.Context, sink TranscriptSink) context.Context

WithTranscript arms one attempt's transcript. The sink belongs to the attempt, not to the worker: a retried leaf gets a fresh one, so its second run appends behind its first rather than merging with it.

func Working

func Working(ctx context.Context) func()

Working opens a span: something this worker is waiting on has started, and the returned function says it is over. Calling the returned function more than once is safe, and so is ignoring the whole mechanism — with nobody listening both halves are no-ops.

It is deliberately shaped so the call site reads as a defer:

defer Working(ctx)()

Types

type AIOptions

type AIOptions struct {
	// Schema constrains the answer. When it is set the answer comes back in
	// [Answer.JSON] as well as in [Answer.Text], and a model that could not
	// produce the shape is an error rather than a text answer quietly standing
	// in for a structured one.
	Schema Schema
	// Effort is the reasoning knob, in the provider package's own vocabulary
	// rather than a second spelling of it. The zero value is [provider.EffortNone]
	// — send nothing and let the model use its own default — which is not the
	// same request as [provider.EffortOff].
	Effort provider.Effort
}

AIOptions is what one model call may ask for beyond its prompt and its input.

type Abandoned

type Abandoned struct {
	// After is the watchdog it outlived.
	After time.Duration
}

Abandoned is the node watchdog's own ending: the executor was still inside a worker that had already run past every limit it was given, and the runner stopped waiting for it.

It is a type rather than fmt.Errorf so that the fact it carries — the clock ran out, nothing about the work refused — survives the trip to whoever decides what happens next. A retry reading this by matching on the words "abandoned" would be a second, private answer to a question the ending already answers, and the first thing to go wrong with a second answer is that it disagrees. The message is unchanged from the sentence this replaced.

func (*Abandoned) Error

func (a *Abandoned) Error() string

func (*Abandoned) Timeout

func (a *Abandoned) Timeout() bool

Timeout satisfies the same interface net.Error uses, which is how a caller asks "was this the clock?" without knowing which layer answered.

type Account

type Account struct {
	// Files is what the work changed, one row per path, merged across however
	// many times the worker touched it.
	Files []FileChange
	// Checks is what the worker's own verifier ran, as of the last time it ran.
	// It is replaced rather than appended by a repeated verification pass: a
	// suite that ran four times reports the state of the fourth run, and the
	// three before it are history rather than evidence.
	Checks []Check
	// Commands is the bounded list of shell commands the leaf ITSELF ran. It is
	// distinct from Checks, which is the closing photograph's reading of the
	// finished tree: no command here is evidence that anything passed because
	// nothing parsed its output, and Verified does not read this field.
	Commands []string
	// CommandsRun is how many commands the leaf itself ran in total, including
	// the earlier commands omitted from the bounded list above.
	CommandsRun int
	// Final is the last thing the worker said about the whole job, verbatim.
	Final string
	// Unread is why the finished tree could not be read, when something was
	// asked of it and the answer could not be understood — a strategy that
	// would not start a second time, a suite that failed to collect, a command
	// killed at its ceiling before it named a check. Empty when nothing was
	// attempted, and that emptiness is what tells [Account.rows] apart the two
	// worlds an absent Checks list used to collapse. It is
	// Verification.Unread's own sentence, carried rather than recomposed, so
	// the account and the reading cannot come to say different things about the
	// same failure.
	Unread string

	// Range is the two commits this account's change set was measured between,
	// when it was measured from the repository rather than narrated by the
	// worker as it went. See [Range] for why the difference is the whole point.
	Range Range
	// Patch is where the change set's own text lives: a path to the diff, in
	// full, written under the harness's own directory for this node.
	//
	// It is a handle rather than the bytes because a diff is unbounded and
	// every reader downstream is bounded — but it is a handle to CONTENT, which
	// is the thing the file list never was. A judge asked whether the
	// deliverable's account of the change is true can read the change; a method
	// writer handed the goal of describing it can read it instead of inferring
	// it. Empty when nothing was derived, which reads as no claim.
	Patch string

	// Withheld is where this change set actually IS, when it is not in the
	// workspace anybody else can open: the directory of the leaf's own view and
	// the branch its work sits on. Empty is the ordinary case and means the
	// change set is in the shared tree.
	//
	// It exists because a change set has two properties that were being read as
	// one. Whether work was DONE is answered by the repository — files, a range,
	// a diff — and stayed true of a leaf whose delivery gate failed, because the
	// leaf really had written all of it. Whether the work is WHERE ANYBODY CAN
	// SEE IT is a different question with a different answer, and nothing asked
	// it. A measured run finished a complete implementation inside its view,
	// failed the gate, merged nothing, and ended with an empty workspace and no
	// sentence anywhere naming the checkout the work was in: 67% of that run's
	// spend, on files the person never saw.
	Withheld       string
	WithheldBranch string
}

Account is a worker's own account of the work it did, in the one shape every reader downstream needs and none of them can reconstruct.

It exists because of a measured, expensive silence. A subharness that drives a whole pipeline behind a process boundary used to hand back one sentence and a bill, and when the pipeline ended without a verdict of its own the sentence was "it ended without saying how it went" — a void. Three readers then had to act on that void: the delivery gate judged it and reacted differently every time, the remainder judge could not tell finished work from unstarted work and added children to re-investigate what was already done, and the person reading the record was told nothing at all. Between 48% and 57% of a run's cost was measured going into that re-investigation.

So the account is structural rather than prose: the files the work changed with the kind of change and its size, the commands it issued itself, the checks the closing photograph ran with what each one found, and the last thing the worker said for itself. Nothing here is interpreted — every field is a measured fact no reader further down could observe for itself — and nothing here is worker-specific. Any subharness that owns a verifier can fill it in, which is why it is named for what it is rather than for who writes it first.

A nil Account is the ordinary case: a worker that photographs nothing leaves it empty, and an empty account reads as "no claim" everywhere it is rendered.

func (*Account) ChangeRange

func (a *Account) ChangeRange() string

ChangeRange names the span in one clause for a reader who is about to be shown its contents, so a truncated diff can still be placed in the repository's own history. Empty when nothing was measured.

func (*Account) CheckLines

func (a *Account) CheckLines() []string

CheckLines renders the verification story as evidence rows: one line per command, the verdict first so a reader scanning the left edge sees what happened before it sees what was run.

A failure that was already there before the work began says so on its own line rather than being dropped. Dropping it would make the account claim a green suite over a suite anyone can watch failing, which is the fastest way to teach a reader that the account cannot be trusted.

func (*Account) CommandLines

func (a *Account) CommandLines() []string

CommandLines renders what the leaf itself ran, separately from the closing photograph's checks. The total names any earlier commands the bounded record left out, so its tail cannot be mistaken for the whole run.

func (*Account) Empty

func (a *Account) Empty() bool

Empty reports that there is nothing here to show. A nil account is empty, so every caller can ask without guarding first.

func (*Account) FileLines

func (a *Account) FileLines() []string

FileLines renders the changed files as evidence rows: the arithmetic first, then one row per path, then the count of whatever was left out. Empty when the work changed nothing, which is itself a fact and belongs to whoever is composing rather than being invented here.

func (*Account) Landed

func (a *Account) Landed() bool

Landed reports that this account's change set came out of the repository and is not empty: the work is on disk, in commits, and can be read by anyone.

It is the fact the repair path turns on. A gate that fails a finished coding leaf for what its PROSE did not say used to buy a second run of the whole engine, into a tree where the change had already landed — 23 model calls and zero edits, because there was nothing left to do. This is how that case is recognised without asking a model: the substrate says the work exists.

func (*Account) Lines

func (a *Account) Lines() []string

Lines is the whole account as evidence rows, headed so each block says what it is. This is what the delivery gate is handed: rows, in the same grammar as the run tail beside them, and never a paragraph explaining what to make of them — what to make of them is the gate's job and the gate's alone.

The worker's own last word is included HERE and nowhere else, because the gate is often judging a deliverable that has been composed since — a synthesis, a repair pass, a parent's answer — and the sentence the worker signed off with may no longer be anywhere in it.

func (*Account) Note

func (a *Account) Note(path, change string, added, removed int)

Note records one file the work touched, merging it with whatever is already known about that path. The line counts accumulate because each call is a separate edit that really did add and remove those lines; the kind takes the strongest word, which is what changeRank is for.

func (*Account) Report

func (a *Account) Report() string

Report is the same account as a block of text, for the leaf's own deliverable. It is the account and nothing else — no verdict, no advice — because the sentence above it already said how the run ended and this is the evidence for it.

It leaves the worker's last word out for exactly that reason: the leaf's own text OPENS with it, and an account that repeated it would put the same paragraph twice into every context the deliverable travels through.

func (*Account) SetFiles

func (a *Account) SetFiles(files []FileChange)

SetFiles replaces the change set outright, and it is the counterpart to Note rather than a convenience beside it.

Note accumulates because each call is a separate edit that really did add those lines, and that is the right law for narration arriving on a wire. It is the wrong law for a derivation: a change set computed from two commits is already the whole answer, and merging it into whatever the narration had said would double every line it agrees with. So the two channels do not mix — the derived one, when there is one, is the account.

func (*Account) Stat

func (a *Account) Stat() (files, added, removed int)

Stat is the diff in three numbers: how many paths, and the lines added and removed across all of them.

func (*Account) Summary

func (a *Account) Summary() string

Summary is the account in one clause, for a reader with room for a clause rather than a block: the plan's own state view, where every node gets one line and thirty nodes share a budget.

func (*Account) Verified

func (a *Account) Verified() bool

Verified reports that the worker ran checks and every one of them settled. It is the fact the remainder judge exists to be told: a node whose suite is green is finished, and a child added to re-run those tests is money spent proving something already proved.

A worker that ran no checks answers no, because "nothing was checked" and "everything passed" are the two answers a silence used to collapse into.

func (*Account) WithheldWords

func (a *Account) WithheldWords() string

WithheldWords is the one sentence a person is owed about work that is real, finished and not in their tree — where it is, and the two things they can do with it. Empty when nothing was withheld.

It is a method rather than a stored string so there is one wording of it, and so the fact and the sentence cannot drift apart: everything it says is read off the fields above.

type Answer

type Answer struct {
	Text string
	// JSON is the structured answer, filled only when [AIOptions.Schema] asked
	// for one. Nil is "nobody asked", never "the model refused" — a refusal is
	// an error.
	JSON json.RawMessage
	// Spend is what this one call cost, in the ledger's own figures. It is
	// returned rather than only journaled so a runner can reason about its own
	// budget without reading back what it just wrote.
	Spend Spend
}

Answer is what one model call produced.

type Artifact

type Artifact struct {
	// Name is what it is called where a person reads it.
	Name string `json:"name"`
	// Path is where it actually is.
	Path string `json:"path"`
	// Note is one line about what it is for, drawn beside it. Nothing renders as
	// nothing.
	Note string `json:"note,omitempty"`
}

Artifact is one file a run produced.

type ArtifactChange

type ArtifactChange string

ArtifactChange is what the before-and-after read of the tree proved about one path. The zero value means the diff never saw it, which is the honest state of a file that only the write tool ever mentioned.

const (
	// ArtifactCreated is a path the tree did not hold when the leaf started.
	ArtifactCreated ArtifactChange = "created"
	// ArtifactChanged is a path whose length or write time moved under the leaf.
	ArtifactChanged ArtifactChange = "changed"
	// ArtifactDeleted is a path the tree held when the leaf started and does not
	// hold now.
	ArtifactDeleted ArtifactChange = "deleted"
)

type ArtifactFact

type ArtifactFact struct {
	// Path is workspace-relative, the spelling everything downstream records.
	Path string
	// Claimed is the worker's own statement, through a write tool, that this
	// file is a deliverable of the work.
	Claimed bool
	// Observed is the filesystem's statement, through the before/after diff,
	// that this file moved while the leaf was running.
	Observed bool
	// Change is how it moved, and is empty when Observed is false.
	Change ArtifactChange
}

ArtifactFact is one path and the two independent things that are known about it: whether a tool claimed it and whether the world was seen to change it.

Callers that only want the file list want Workspace.Artifacts. This exists for the ones that have to tell the two apart — an audit reporting what the worker said it produced against what the disk says happened cannot do its job from a merged list, and merging them is precisely how "the record shows nothing" came to outrank a file that existed.

type AskAnswer

type AskAnswer struct {
	// Text is what they said — the chosen option, or the words they typed.
	Text string
	// TakingOver means the person is continuing by hand. The run ends as
	// incomplete with their note as its report, whatever Text says.
	TakingOver bool
	// Unanswered means nobody was there and no default was declared. A program
	// that reads this stops; it does not guess.
	Unanswered bool
}

AskAnswer is what came back.

THE THIRD ANSWER IS THE POINT, and it is carried over from the gate the old system got right (internal/subharness's GateAnswer): a person watching a program they wrote go slightly wrong does not want to kill it and does not want to wave it through — they want to take it from here. So Taking over ends the run where it stands, with its journal complete, and what they typed becomes the run's answer for whoever picks it up.

type AskOptions

type AskOptions struct {
	// Options are the answers this question offers, if it offers a set. Empty is
	// a free-text question. A surface draws these as chips; a headless run
	// matches a manifest default against them.
	Options []string
	// Default is what an unattended run answers with. EMPTY IS NOT A YES: a
	// question with no default, asked where nobody is, stops the run incomplete
	// at that question rather than choosing for the person.
	Default string
}

AskOptions is what a question may offer beyond a blank line.

type BundleSource

type BundleSource interface {
	// Runner loads the head version of one bundle. Not found is (nil, false),
	// which is not an error — it is the next layer's turn.
	Runner(name string) (Runner, bool)
	// Manifests is everything this source can offer, for the lists and the
	// menus. A source that cannot be read answers nothing: a registry is not
	// worth failing a launch over, and a list is not where a person learns their
	// disk is gone.
	Manifests() []Manifest
}

BundleSource is a place bundles are loaded from — the store lane's hook, and the only one it needs. Registering one at a Layer is the whole of putting a store in front of the registry.

IT IS ASKED, NEVER SCANNED. A source answers one name at a time because the stores are directories on disk that change under a running process: a registry that had cached a listing would run a version the person had replaced, and one that re-listed on every lookup would stat a directory per dispatch. What the source caches, and how it decides a page has moved, is the store lane's business.

type Check

type Check struct {
	Command string
	Kind    string
	Passed  bool
	Known   bool
	Tail    string
}

Check is one command the worker's verifier ran and what the process said.

Known is the one field that is not simply the exit status: a check that came back red and was red in exactly the same places before the work began has not been broken by this work. The worker is the only thing in the system that photographed the repository beforehand, so it is the only thing that can say so, and a reader that treated a known-red suite as this work's failure is the measured way correct work gets thrown away.

type Completer

type Completer interface {
	CompleteWithMessages(ctx context.Context, messages []ai.Message, options ...ai.Option) (*ai.Response, error)
}

Completer is the slice of the provider adapter this package needs.

type ControlAction

type ControlAction string
const (
	ControlNone   ControlAction = ""
	ControlPause  ControlAction = "pause"
	ControlCancel ControlAction = "cancel"
)

type DocumentProvider

type DocumentProvider interface {
	ParseDocument(context.Context, provider.DocumentRequest) (*provider.DocumentResponse, error)
}

type Env

type Env interface {
	// AI is one model call. promptRef NAMES A PROMPT ASSET IN THE BUNDLE and is
	// never an inline string: prompts are files under prompts/ so they can be
	// diffed, reviewed, revised into a new version, and — later — carry their
	// own accepted-output cache per call site. A program that could inline its
	// prompt would put the one thing worth improving somewhere nothing can
	// improve it.
	AI(ctx context.Context, promptRef string, input any, opts AIOptions) (Answer, error)

	// Tool calls one belt tool, filtered by the manifest's whitelist, through
	// the same consent doors any tool call goes through. A tool not on the
	// whitelist is refused here; a tool not on the session's belt is absent, and
	// the two are told apart in the error because they are different facts about
	// what the person can fix.
	Tool(ctx context.Context, name string, args map[string]any) (ToolResult, error)

	// Ask puts a question to the person and waits for the answer. WHAT IT MEANS
	// WITH NOBODY THERE IS DECLARED PER GATE, never assumed: a headless run
	// either takes the answer the manifest defaulted or stops incomplete and
	// says which question it stopped at. The old bridge's auto-approving gate
	// (internal/subharness/exec_model.go) is the anti-pattern this signature
	// exists to make unnecessary — it was kept honest only by writing "nobody was
	// there to ask, so it carried on" into the trail afterwards.
	Ask(ctx context.Context, question string, opts AskOptions) (AskAnswer, error)

	// Remember keeps one note in this subharness's OWN memory — its file in its
	// own bundle, not the session's. A program that has learned that this
	// company's brief always arrives as a PDF has learned something about its
	// domain and nothing about this conversation.
	Remember(ctx context.Context, note string) error

	// Recall reads that memory back. An empty query is everything it has kept.
	Recall(ctx context.Context, query string) ([]Note, error)

	// Log raises one visible progress row into the run's journal, in the
	// program's own words. IT IS THE ONE DOOR TO A PERSON'S ATTENTION that costs
	// nothing, and the vocabulary law reaches it: work is running, finishing,
	// done, incomplete, or your call.
	Log(ctx context.Context, status string) error
}

ONE ENV, TWO CONSUMERS — and this file is the wound being closed.

The tree already had two drifting things called Env: internal/subharness's (exec_model.go, an interface over the five DAG node kinds) and the session's implementation of the same interface over its own belt. The seam between them is written up at cmd/codeaf/chatv3_harness.go as a known wound, in the two absences it produced — a gate with nobody to ask, and a model the conversation could not move. PRD §5 says plainly: do not mint a third. So this is the one, and it is the one for both consumers at once.

  • It is the HOST API handed to a JavaScript bundle. goja has no filesystem, no network and no clock unless the host hands them in, so the six methods below are the ENTIRE capability surface of a bundle. There is no seventh way to spend, reach out, or ask.
  • It is the INTERFACE handed to a Go runner. A subharness compiled into this binary gets the same six doors and no more, which is what makes "the person cannot tell which is which" a structural fact rather than a promise.

EVERY TOKEN AND EVERY DOLLAR FLOWS THROUGH ai() AND tool(). That is what makes the ledger complete without the ledger having to be everywhere: a program cannot spend any other way, so summing the journal's entries is summing the run. And every call here is JOURNALED BEFORE ITS ANSWER RETURNS (journal.go) — an implementation that answered first and wrote afterwards would leave a run killed mid-call with no record of the call that killed it.

The bodies behind these doors are the runtime lane's. UnwiredEnv below is what a caller that has not wired one yet hands over, and it is honest about having nothing behind it rather than pretending to work.

type Event

type Event struct {
	NodeID  int
	Title   string
	State   plan.State
	Detail  string
	Elapsed time.Duration
}

Event is one thing happening to one node.

type Executor

type Executor interface {
	Subharness() string
	Run(ctx context.Context, task Task) (*Outcome, error)
}

Executor runs one task to completion.

Implementations must be safe for concurrent use: the scheduler runs many leaves at once against a single executor, which is the entire point of having built a graph.

type ExecutorRunner

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

ExecutorRunner fronts one Executor as a Runner.

IT IS GENERIC AND MUST STAY SO. Nothing in it may ask which worker it is wrapping — that is the same law that keeps the leaf table a table, and it is what makes the owner's next Go subharness a registration instead of an edit to this file. The executor's own manifest carries the purpose, the ruler and the budget shape; this type carries the translation and nothing else.

It takes no Env. That is not an oversight and it is the honest shape: an executor built by a surface already has its own client, its own workspace and its own belt, and handing it a second capability surface it cannot reach would be a promise the wrapper could not keep. A Go subharness that WANTS the host doors implements Runner directly, which is the whole reason the interface is the contract and this adapter is only a bridge.

func FrontExecutor

func FrontExecutor(executor Executor, manifest Manifest) (*ExecutorRunner, error)

FrontExecutor wraps an executor with the manifest that describes it. The manifest's name has to be the executor's own subharness, because a registry entry filed under one name and dispatching to another is a measurement of the wrong worker wearing the right one's history.

func (*ExecutorRunner) Executor

func (e *ExecutorRunner) Executor() Executor

Executor is the worker underneath, for the dispatch paths that still want it whole — the scheduler's, and the deoptimization path's.

func (*ExecutorRunner) Manifest

func (e *ExecutorRunner) Manifest() Manifest

func (*ExecutorRunner) Run

func (e *ExecutorRunner) Run(ctx context.Context, input json.RawMessage, _ Env) (RunResult, error)

Run turns typed input into a Task, runs it, and turns the Outcome back into typed output.

THE ENDING IS READ FROM THE OUTCOME AND NOT GUESSED. A leaf that ran out of budget lands truthfully on StopDone — that is what Outcome.Exhausted exists to tell apart — so a wrapper that read Stop alone would report a truncated partial as a finished deliverable, which is exactly the failure the Exhausted field was added for. Outcome.Overran is the one question worth asking and it asks both fields.

THE ENV IS TAKEN AND NOT USED, AND THAT IS A FACT ABOUT WHAT AN EXECUTOR IS rather than an oversight — it is named `_` so nobody reads it as a seam that merely has not been wired yet. An Executor is a whole worker with its own client and its own belt (linear.go), not a program stepping through host calls: it never asks for a tool, a model call or a person, so there is nothing here for the Env's doors to serve. The parameter stays because Runner is one interface over both shapes, and [runSpend]'s own note says the same thing from the ledger's side — a fronted leaf reports its spend and journals nothing.

WHAT THAT COSTS IS PAID IN Deopt AND NOWHERE ELSE. Because this belt cannot be filtered, a program's approved ceiling cannot be applied to a worker reached through here — so the decision about whether the fallback may run at all is taken before this function, on the program's own manifest (DeoptHeld). Nothing about that decision belongs in here: this function serves the ordinary path too, where the manifest IS this runner's own.

type Field

type Field struct {
	Name        string
	Type        string
	Title       string
	Description string
	Required    bool
	// Default is what the schema says to use when nobody says otherwise, in its
	// own JSON. It is nil when the schema named none, which the card draws as
	// nothing rather than as a guessed blank.
	Default json.RawMessage
	Enum    []string
}

Field is one line of the intake card: what to ask for, whether it has to be answered, and what it means. It is the reason Schema is read at all in this package.

type FileChange

type FileChange struct {
	Path    string
	Change  string
	Added   int
	Removed int
}

FileChange is one path the work touched: what kind of change it was, and how large. The line counts are the worker's own, taken from the patch it applied, rather than a diff computed afterwards by somebody who was not there.

type Governor

type Governor struct{}

Governor is the admission gate on new leaves. It never blocks: a refusal is a decision the caller's next tick re-asks, so there is no hold to bound and no way for the gate to wedge a runner that is otherwise ready to work.

func HostGovernor

func HostGovernor() *Governor

HostGovernor is the shared gate every claim path consults.

func NewGovernor

func NewGovernor() *Governor

NewGovernor builds the gate. Callers share one: what is being bounded is a process-wide resource, and a per-runner gate would each count only its own share of it.

func (*Governor) Admit

func (g *Governor) Admit(inFlight int) bool

Admit reports whether one more leaf may be claimed. inFlight is how many leaves the caller is already running.

Host load is deliberately absent from this decision. A leaf is a socket and a goroutine; the resource it consumes belongs to the provider, not to this machine, and the provider's limiter is what adapts to it. The only thing asked here is whether the process is about to run out of the cheap local resources a socket does cost.

type Guard

type Guard struct {
	Kind GuardKind `json:"kind"`
	// File, Tool and Field name what is being looked at, one per kind.
	File  string `json:"file,omitempty"`
	Tool  string `json:"tool,omitempty"`
	Field string `json:"field,omitempty"`
	// Pattern is what a [GuardField] field has to match. It is a plain substring
	// test in the checker the runtime lane writes; a regular expression in a
	// manifest is a program a person did not know they were writing.
	Pattern string `json:"pattern,omitempty"`
	// Question is the whole of a [GuardJudgement]: one question, one word back.
	Question string `json:"question,omitempty"`
	// Because is what this check is FOR, in a person's words — "this one needs
	// the brief in the repository". It is the sentence the fallback line is
	// built from, and a guard without one falls back silently rather than
	// explaining itself in machinery vocabulary.
	Because string `json:"because,omitempty"`
}

Guard is one cheap precondition, checked before the program is entered.

THE FAILURE IS A FALLBACK AND NOT AN ERROR. When a guard does not pass, the work still gets done — by the general worker, with the original input — and what the person is told is that the step needed a closer look and was handled the long way. Nothing here may be drawn with the word "guard" in it, and Guard.Because exists so that the sentence a person reads was written by somebody who knew what the check was for.

func (Guard) Validate

func (g Guard) Validate() error

Validate says whether one guard is checkable at all, in the prose the author of the manifest needs to hear.

type GuardKind

type GuardKind string

GuardKind is what a guard actually checks. The four kinds are the cheap ones the PRD allows and no more: three are decided by looking, and the fourth is permitted one tiny model call. A guard that would cost real money is not a guard, it is the first step of the run.

const (
	// GuardFile passes when the named path exists.
	GuardFile GuardKind = "file"
	// GuardTool passes when the named tool is actually on this session's belt.
	GuardTool GuardKind = "tool"
	// GuardField passes when the named input field matches Pattern.
	GuardField GuardKind = "field"
	// GuardJudgement is the one rung that costs: one small question, answered
	// yes or no, about the input in hand. It is last because everything above it
	// is free.
	GuardJudgement GuardKind = "judgement"
)

type Input

type Input struct {
	Title     string
	Result    string
	Artifacts []string
	// Whole says Result above is the producer's material and not an account of
	// it: the files were read back and their text is in the block. It exists
	// because the sentence under that block is an instruction either way, and
	// the two instructions are opposites — "read them if you need the full
	// detail" is an invitation to go and get what the leaf is already holding.
	// False is the older and weaker claim, and is what every caller that does
	// not set this keeps.
	Whole bool
}

Input is one upstream result routed into a task.

This is the dependency list finally doing its runtime job. A node receives exactly the results of the nodes it declared and nothing else, so the edges that were argued over during planning are the same edges that decide what an agent can see. Title names the producer. It is not decoration: the block these are rendered into is headed "results from earlier work, which you already have and must not gather again", and an untitled entry reads as an anonymous claim about what has already been done.

There was once a Summary field here as well, written by the scheduler and read by nothing. It is gone rather than rendered: a field that describes an input but never reaches the agent is a claim about what the agent knows that is simply untrue.

type Journal

type Journal interface {
	Write(entry JournalEntry) error
}

Journal is where a run's entries go. It is one method because it has one job, and the incremental writer behind it — the file, the flush discipline, the progress events it raises on the way past — is the runtime lane's to build.

A NIL JOURNAL IS A RUN NOBODY IS WATCHING, which is a real case: a guard check before the run, a headless invocation whose caller wants only the answer. Use Record rather than calling Write on a possibly-nil interface, so that no host implementation has to carry the nil check itself.

type JournalEntry

type JournalEntry struct {
	// Seq counts from one within a run, so a reader can order entries without a
	// clock. Two entries written in the same millisecond are still in order.
	Seq int       `json:"seq"`
	At  time.Time `json:"at"`
	// Call is which door was used: "ai", "tool", "ask", "remember", "recall" or
	// "log". The constants are below, and they are spelled the way the program
	// spells them so a person reading a journal and a person reading the bundle
	// are reading one vocabulary.
	Call string `json:"call"`
	// Ref is what the call was about, in one string: the prompt asset an ai()
	// named, the tool a tool() asked for, the question an ask() put, the note a
	// remember() kept. It is what a compact progress row is drawn from.
	Ref string `json:"ref,omitempty"`
	// Input and Output are the call's two halves as JSON. THEY ARE NOT CLIPPED
	// HERE: clipping is a decision about a surface, and a journal that had
	// already thrown the material away could not be the resume point it is also
	// meant to be. A writer with a size law of its own applies it on the way to
	// disk and says so.
	Input  json.RawMessage `json:"input,omitempty"`
	Output json.RawMessage `json:"output,omitempty"`
	// Spend is what this one call cost. Summed across a run's entries it is the
	// run's whole ledger, which is the only reason the ledger and the journal
	// can never disagree.
	Spend Spend `json:"spend,omitzero"`
	// Note is the one line a person reads for this row, in a person's words. It
	// is where a log() status lands, and it is what a surface draws when it has
	// room for one line and not for a call.
	Note string `json:"note,omitempty"`
	// Err is what went wrong, when something did. A call that failed is still a
	// call that happened and still costs what it spent before it failed.
	Err     string        `json:"err,omitempty"`
	Elapsed time.Duration `json:"elapsed,omitempty"`
}

JournalEntry is one host call, whole.

type Layer

type Layer int

Layer is a place a subharness can be found, in the order the registry looks.

THE ORDER IS THE LOOKUP ORDER AND IT LIVES HERE ONCE. PRD §7 states it and nothing else in the tree is allowed to restate it: a source registers itself at a layer and the registry sorts by the constant, so adding the packed trailer in a later phase is a registration at LayerPacked and not an edit to any lookup.

const (
	// LayerBuiltIn is the compiled-in Go runners. They are layer zero — before
	// every store — and that is deliberate: a bundle on disk MAY NOT SHADOW A
	// NAME THIS BINARY SHIPS. The names the owner ships are the names the manual
	// and the prompts describe, and a store that could silently replace one
	// would make both of those documents a lie about the program that actually
	// ran.
	LayerBuiltIn Layer = iota
	// LayerPacked is a zip appended to the binary itself. It is Phase 2 and
	// nothing in this tree writes or reads one yet; the constant exists so the
	// phase that does is a registration rather than a renumbering.
	LayerPacked
	// LayerProject is `.codeaf/subharnesses/` inside the repository in hand.
	LayerProject
	// LayerHome is `~/.codeaf/subharnesses/`, which moves with CODEAF_HOME the
	// same way the page store below does.
	LayerHome
	// LayerPages is `~/.codeaf/harnesses/`, where a subharness written as a PAGE
	// lives — the shape the design flow saves when somebody asks for a program to
	// be built for them (internal/subharness's store.go owns the layout).
	//
	// ONE CATALOG, SEVERAL PLACES A PROGRAM CAN LIVE. A page is not a second kind
	// of subharness and nothing downstream of this constant may treat it as one:
	// it is found here rather than one directory over, it is the person's own
	// ([Layer.Provenance] answers `yours` for it as it does for the home store),
	// and every list draws it beside the bundles and the compiled-in workers
	// without a word about which is which.
	//
	// It is LAST because a page declares no schemas of its own, so a name carried
	// by both stores is better served by the bundle: the lookup order is the whole
	// of that decision and it lives here.
	LayerPages
)

func (Layer) Provenance

func (l Layer) Provenance() Provenance

Provenance is the word a person reads for a layer. The packed trailer answers the same word the home store does on purpose: what a person wants to know from the mark is whether a program is the owner's, theirs, or their team's, and a bundle they packed into a binary themselves is still theirs.

type Linear

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

Linear is a single agent working in order: think, call tools, look, repeat.

func NewLinear

func NewLinear(client Completer, workspace *Workspace, web *Web, maxTurns, maxTokens int, deadline time.Duration) *Linear

NewLinear builds the loop. maxTokens is the limit that actually binds; maxTurns is the runaway backstop, clamped to maxTurnBackstop however high a caller asks.

Counting turns was the wrong meter. Turns are not what a loop spends — one 25-turn leaf cost more than the other nine nodes of a run put together, because cost tracks accumulated context rather than iteration count. Bounding tokens lets a task take all the small steps it needs while still stopping one that is genuinely expensive. What the turn cap is for is the case tokens cannot see: a loop whose turns have become cheap enough that no token bound arrives in useful time.

func (*Linear) ContextLength

func (l *Linear) ContextLength() int

ContextLength answers what this loop's model holds, for the callers that must size something for it before it runs — the scheduler bounding an upstream result on its way in, principally. Zero is the honest unknown, exactly as WithContextLength leaves it, and every reader has a named fallback for that.

func (*Linear) Run

func (l *Linear) Run(ctx context.Context, task Task) (returned *Outcome, runErr error)

Run executes one task.

func (*Linear) Subharness

func (l *Linear) Subharness() string

func (*Linear) WithAssistedBy

func (l *Linear) WithAssistedBy(model string) *Linear

WithAssistedBy names the model the contract's `Assisted-by` line credits. The surface passes the model this leaf runs on, or the empty string when the person turned the model's name off (internal/config's AssistedByModelAt), and the line is then the bare `Assisted-by: CodeAF`. The attribution law itself is in every contract whatever this is handed.

func (*Linear) WithContextLength

func (l *Linear) WithContextLength(tokens int) *Linear

WithContextLength tells the loop how much its model can actually hold, in tokens, so the observation window can be sized from it.

It is a separate setter rather than a constructor argument because the answer comes from the provider's catalog, which the surface owns and this package deliberately does not: exec is handed facts about the model, never a client it has to interrogate. An unknown or unavailable model is zero, which is not an error — the window has a default for it, and a leaf must never fail to run because a metadata endpoint was down.

func (*Linear) WithMedia

func (l *Linear) WithMedia(media *MediaTools) *Linear

WithMedia installs graph-level image, music, video, speech, and image-inspection tools. It is executor configuration, so reflex micro-leaves inherit it unchanged.

func (*Linear) WithStore

func (l *Linear) WithStore(history *store.Store) *Linear

WithStore enables the optional persistent-memory pull tool, and it is also where this loop's readings of the project's own checks are journaled. It mutates the just-constructed loop for fluent wiring; callers that do not opt in retain the base-tool completion floor, and their readings are still taken and still weighed, with nowhere to write the row down.

func (*Linear) WithSwarm

func (l *Linear) WithSwarm(on bool) *Linear

WithSwarm arms the cooperative division tool for this loop.

It is a setter carrying a settings row, exactly as WithAssistedBy is, and for the same reason: the surface owns config and this package is handed facts. Off is the absence of request_split from the schema rather than a paragraph saying not to divide — a worker that has never been told it can hand work back does not hand work back, and the belt is where that is said.

type LivenessMark

type LivenessMark func() func()

LivenessMark is told that a call is starting and returns the function that says it finished. An implementation must be safe for concurrent use: a batch of tools runs in parallel, so several spans are open at once.

type Manifest

type Manifest struct {
	// SubharnessInfo is the identity and the COST SHAPE, unchanged and in the
	// same spelling every existing caller reads: the name, the one line of
	// purpose, the capacity ruler the sizing pass quotes, and the three fields
	// [SubharnessInfo.Deadline] turns into a leaf's hang backstop. The PRD names
	// this pair — prior anchors plus deadline shape — as the manifest's cost
	// shape and says to keep the linearInfo form. Embedding is how it is kept:
	// there is no second spelling of a budget anywhere in this file.
	SubharnessInfo

	// Cues are the trigger vocabulary a match is made against — the words that
	// mean this work, written down at design time and PERSISTED with the
	// program. Their absence from disk is a real hole in the old system, named
	// at internal/session/harness_build.go where a designed harness's cues die
	// with the session that designed them and the page it saved carries none.
	// A manifest with no cues is found by being NAMED and by nothing else, which
	// is a quieter subharness rather than a broken one.
	Cues []string `json:"cues,omitempty"`

	// Input is the typed front door: a JSON Schema with defaults and optional
	// fields, which the intake card draws field by field and chat fills from the
	// conversation. A Go runner may derive it by reflection over a struct; a
	// bundle declares it. Both arrive here as the same bytes.
	Input Schema `json:"input,omitempty"`

	// Output is the PROMISE, and it is the machine-checkable finish line the old
	// one-string program form never had. A run that ends without producing this
	// shape is INCOMPLETE — never done with a strange message — and that
	// sentence is enforceable only because the shape is written down here.
	Output Schema `json:"output,omitempty"`

	// Whitelist is the belt tools this subharness may call through [Env.Tool].
	// It is a ceiling and not a grant: a tool named here that the session does
	// not have is simply not there, and every call still goes through the same
	// consent doors an ordinary tool call goes through. An empty whitelist is a
	// subharness that spends only on the model.
	Whitelist []string `json:"whitelist,omitempty"`

	// Guards are the cheap preconditions checked before a run — a file that has
	// to exist, a tool that has to be on the belt, an input field that has to
	// look a certain way. A FAILED GUARD IS NOT A FAILED RUN: the run falls back
	// to the general worker with the same input, and the person is told the step
	// needed a closer look. See [Guard] for the vocabulary law that governs what
	// they are allowed to say.
	Guards []Guard `json:"guards,omitempty"`

	// Provenance is where this subharness was found: shipped in the binary,
	// written by the person, or committed into the repository they are standing
	// in. It is drawn as a dim mark on the list and it is the one field a person
	// reads that this file spells in words rather than in a code.
	//
	// PROVENANCE IS STAMPED, NEVER AUTHORED. A manifest.json on disk does not get
	// to claim it is built in; the registry writes this field from the layer the
	// bundle was actually loaded out of ([Layer.Provenance]), which is why
	// [Manifest.Validate] neither requires it nor objects to it.
	Provenance Provenance `json:"provenance,omitempty"`
}

THE MANIFEST: everything about a subharness that is true before it runs.

docs/SUBHARNESS-PRD.md §3 asks for one language-agnostic description shared by the subharnesses compiled into this binary and the ones a person writes as a JavaScript bundle, and it asks for it as a GROWTH of SubharnessInfo rather than as a second type beside it. That is what the embedding below is. Every reader that has ever asked a registration for its name, its purpose, its ruler or its budget shape still asks the same fields of the same struct; a manifest is that struct plus the six things a TYPED program needs and a leaf worker never did — the schemas, the whitelist, the cues, the guards, and where it came from.

ONE NAMESPACE ACROSS GO AND JS, which is the decision the whole contract rests on. There is exactly one map from a name to a subharness in this process, so a bundle on disk and a worker compiled in are the same kind of thing to the person, to the model, and to every list either of them reads. Which language ran it is a fact about the runner and reaches no surface.

func LeafManifest

func LeafManifest(info SubharnessInfo) Manifest

LeafManifest grows the leaf worker's registration into a manifest.

A LEAF WORKER IS HANDED A JOB AND PRODUCES AN OUTCOME, so it has the same typed front door and the same typed promise as anything else under this contract — TaskInput and TaskOutput — and stating that here, once, is what re-fronts the worker under the subharness contract without a single caller changing a line.

It leaves cues, whitelist and guards empty, and that is honest rather than unfinished: the worker is what a job gets when nothing was named, its tools are the belt its surface built it with, and there is no cheap precondition to check before handing somebody a general agent. A saved program that wants any of the three fills them in and registers through [RegisterManifest] instead.

func (Manifest) Validate

func (m Manifest) Validate() error

Validate says whether a manifest is one, in the words its author needs.

EVERY MESSAGE IS A SENTENCE ABOUT WHAT IS MISSING, because the two readers are a person writing a bundle by hand and a model iterating against the error it got back. Neither is served by a code, and the model is served badly enough by a code that it will invent a field name to satisfy it.

type MediaProvider

type MediaProvider interface {
	GenerateImage(context.Context, provider.ImageRequest) (*provider.ImageResponse, error)
	Speak(context.Context, provider.SpeechRequest) (*provider.SpeechResponse, error)
	// GenerateMusic is its own lane and not Speak with a music model in it.
	// This tool sent composition briefs to /audio/speech for as long as it has
	// existed, and that endpoint has no music behind it — the router composes
	// through streaming chat completions instead
	// (internal/provider/music.go). Every call generate_music made failed.
	GenerateMusic(context.Context, provider.MusicRequest) (*provider.MusicResponse, error)
	GenerateVideo(context.Context, provider.VideoRequest) (*provider.VideoResponse, error)
}

type MediaTools

type MediaTools struct {
	Provider    MediaProvider
	Catalog     ModalityCatalog
	ImageModel  string
	SpeechModel string
	MusicModel  string
	VideoModel  string
	VisionModel string
	// VisionClient is a direct completion client. The selected VisionModel is
	// applied per request so capability resolution can stay live at leaf start.
	VisionClient Completer
	// DocumentClient owns the explicit OpenRouter file-parser completion. It is
	// separate from ordinary model turns so every request names its cost rung.
	DocumentClient DocumentProvider
	// DocumentEngine is auto, local, free, or ocr. Empty is auto for embedders
	// that construct MediaTools directly.
	DocumentEngine string
	// VideoPrice is the catalog's fixed per-request price when advertised.
	// Zero means the catalog had no trustworthy estimate; the rail still runs.
	VideoPrice   float64
	WorkingModel string
	BeforeSpend  func(context.Context, float64) error

	// ResolveModel reads a media tool's optional model argument against the
	// live catalog: "best" for the modality's strongest advertised model, or a
	// name to resolve inside that modality. The returned error is already
	// user-facing prose. Nil means the slot default is the only choice, which
	// is what an embedder without a catalog gets.
	ResolveModel func(modality, word string) (string, error)
}

MediaTools is the leaf-wide capability bundle. BeforeSpend is the same policy gate used before ordinary work launches; its amount is a catalog estimate when one is known, or zero for the standard gate path. Nil means no dollar rail.

type Meter

type Meter struct {
	// Name is the bound in one word, for a reader and for a grep. The bounds the
	// loop writes have constants because two files spell them and one of them
	// decides whether the figures are re-read at land time: see MeterCost.
	Name string `json:"name,omitempty"`
	// Reached and Allowed are the bound's own two numbers, in the bound's own
	// unit. Zero Allowed means the bound has no figure worth printing (a
	// structural detector rather than a ceiling).
	Reached int `json:"reached,omitempty"`
	Allowed int `json:"allowed,omitempty"`
	// Unit is what those numbers count, so a line can be composed without the
	// reader having to know which bound spells its allowance in what: "tokens",
	// "turns", "prompt tokens sent".
	Unit string `json:"unit,omitempty"`
}

Meter is one bound, named, with what it reached and what it allowed.

It is a value rather than a sentence because the two readers want different things from it: the journal wants the figures so a later run can be compared with this one, and the person wants a line. Composing the line from the figures keeps the two from disagreeing, which is the whole of the defect it answers — the ink run of 2026-08-29 printed a grant of 150,000 beside a leaf that had been landed by a different ceiling at 240,000.

func (Meter) Named

func (m Meter) Named() bool

Named reports that a bound actually said something.

func (Meter) Words

func (m Meter) Words() string

Words is the bound in a person's own sentence, with its figures.

type ModalityCatalog

type ModalityCatalog interface {
	Supports(modelID, direction, modality string) bool
}

type Mutator

type Mutator interface {
	// Mutates reports that this worker's deliverable is a change to the
	// workspace. It is a property of the worker and never of one run.
	Mutates() bool
}

Mutator is a worker whose product is a change to the workspace itself rather than a message about it.

It is a capability interface rather than a method on Executor because it is a fact about a minority of workers and every reader of it is optional: a build with only the generalist answers no to everything here and behaves exactly as it did. The distinction it draws is the one that decides whether a second attempt at a leaf is worth anything. A worker that produces prose can always produce better prose by being run again; a worker that produces a diff, whose diff has already landed and whose checks are already green, cannot — running it again re-executes a whole pipeline against a tree where the work is finished, which was measured at 23 model calls, zero edits and 80% of the leaf's spend.

It is deliberately a question about the executor rather than a name: nothing here names a worker, so a mutating program is a registration and not an edit to the repair path.

type NotWired

type NotWired struct{ Call string }

NotWired names which door was unwired. Its message is developer-facing and says so plainly — a person never sees one of these, because a capability that cannot work is absent from the surface rather than present and failing.

func (*NotWired) Error

func (n *NotWired) Error() string

func (*NotWired) Unwrap

func (n *NotWired) Unwrap() error

type Note

type Note struct {
	Text string
	// At is when it was kept, as an RFC 3339 string rather than a time, because
	// this is what a bundle's memory.md carries and a program reads it as text.
	At string
}

Note is one thing a subharness has remembered about its own domain.

type Opening

type Opening struct {
	// Reading is the project's own account of whether it still works, read
	// while the tree was still pristine.
	Reading verify.Reading
	// Moved says THE JOB HAS CHANGED FILES SINCE THIS READING WAS TAKEN, and it
	// is what the second reading is bought with. See verify.TreeState.
	//
	// This field carried "did this leaf inherit a reading" when it was first
	// written, and that was the wrong question in the way #460 measured: every
	// leaf after the first inherits, so a job whose leaves were ordered to
	// change nothing bought a whole second reading at every one of them — four
	// of them, two minutes each, over a tree that never moved. Inheriting is a
	// fact about a lookup; this is a fact about the tree, and the tree is what
	// the reading is of.
	Moved bool
	// Base is the commit the repository was standing on at that moment, and it
	// is the only one of the three that cannot be recovered later — by the time
	// a leaf lands, HEAD is wherever the leaf left it. Empty for every root that
	// is not a git work tree, which reads downstream as no claim.
	Base string
}

Opening is everything the seam BEFORE the work took away with it, carried in one value to the seam after it.

It is one value rather than three parameters because the three are one fact — what this leaf found when it arrived — and the belt that carries them threads them through ten separate exits. Two of them were carried that way and the third was not carried at all: nothing anywhere remembered where the repository stood when the tree was photographed, so Account.Range had no commit to be measured from and Account.Landed answered no for every run in the world. A fact that has to be added to ten call sites is a fact that gets added to nine.

func PhotographBefore

func PhotographBefore(
	ctx context.Context, workspace *Workspace, history *store.Store,
	wall time.Duration, task Task,
) (opening Opening)

PhotographBefore is the reading the whole comparison is subtracted from, and it is the JOB's reading rather than this leaf's. What it hands back is the whole Opening — the reading, whether the job has moved the tree since it was taken, and where the repository's history stood — because all three are carried to the same seam.

A repair round is a new leaf, in a new workspace object, standing in a tree its own job has already changed. A leaf that photographed what IT found took the broken tree as its baseline, so every check an earlier round turned red subtracted to nothing and was never a finding again — textual's s5 run walked twenty project checks down to one across four rounds and raised nothing. So the baseline is looked up first: where this job already settled what its tree looked like — a reading, or the reason there could not be one — this leaf inherits that answer and spends nothing on reaching it again.

A first reading of a tree runs BEFORE Workspace.WatchTree deliberately. A test runner leaves its own droppings — a .pytest_cache, a target/, a coverage file — and a reading taken after the tree was photographed would file every one of them as something this leaf produced. Taken first, they are part of the world the leaf arrived in, which is what they are. Every belt that calls this owes it that ordering.

EVERY OUTCOME IS JOURNALED, including every way of having no reading. That is the whole repair of the s6 silence: a leaf spent five minutes and twenty-seven seconds on a reading that was killed at its ceiling, and the finished store held no row saying so, which read from outside exactly like a project that declares no verification at all.

The worker's own wall is passed in rather than read off a belt, because the three belts hold it under three different names and a shared measurement must not have to know which one it is standing in. A nil history is ordinary — a leaf run outside a graph has no journal to write into — and the reading is taken and weighed identically with or without one.

The opening's own Moved is the other half of the answer, and it is what the second reading is bought with: THE JOB HAS CHANGED FILES SINCE THIS READING WAS TAKEN. It used to say "this leaf inherited a reading", which is a fact about a lookup and not about a tree — every leaf after the first inherits — so a job whose leaves were ordered to change nothing bought a whole second reading at every one of them. The measured errand spent four of them, two minutes each, on a tree that never moved. See verify.TreeState.

type Outcome

type Outcome struct {
	Text      string
	Artifacts []string
	Turns     int
	ToolCalls int
	Decayed   int // observations faded to stubs, a measure of how much context was reclaimed
	// Folded counts the leaf's own aged assistant turns retired to pointers. It
	// is separate from Decayed because the two say different things about a run:
	// decay means the work produced more raw material than fits, fold means the
	// work produced more of its own prose than fits, and only the second is
	// evidence that a leaf was talking to itself.
	Folded int
	// Steered counts the user's mid-flight lines this run actually read. It is
	// the difference between a redirection delivered to a mailbox and one
	// delivered to a mind, and it is zero on every leaf nobody steered.
	Steered int
	Usage   Usage
	// PerTurn is Usage with its shape kept: one row per turn, summing to Usage
	// exactly. See meter.go for why a summed row alone cannot answer the
	// question anybody asks of it. Executors that do not meter turns leave it
	// empty, which reads as "no shape recorded" rather than as a leaf with no
	// turns.
	PerTurn []TurnUsage
	// Meter is the bound that actually landed this leaf, with its own two
	// numbers.
	//
	// THREE CEILINGS PRODUCED ONE SENTENCE. A leaf could be landed by its cost
	// grant, by an undiscounted token ceiling three times that grant, or by a
	// cumulative bound on prompt sent — and all three set Exhausted to
	// StopBudget, so the journal said "it ran out of its tokens" and the surface
	// printed the grant, which in the ink run of 2026-08-29 was a number the
	// leaf never came near. An autopsy could not tell which ceiling had fired,
	// and the first three readings of that run each blamed a different one.
	//
	// A MEASUREMENT THAT WAS NOT TAKEN IS A FACT ABOUT THE RUN (FAILSAFE.md's
	// sixth failure) and so is a measurement whose meter nobody can name. Empty
	// on a leaf that was not landed by a bound.
	Meter Meter
	Stop  StopReason
	// Exhausted is what ran out, when something did. It is separate from Stop
	// because the two answer different questions and the common case makes them
	// disagree: a leaf whose budget runs out is told to land, it lands, and it
	// ends StopDone — truthfully, because it did stop asking for tools. Reading
	// that as an ordinary finish was how the whole continuation subsystem came
	// to be dead on its designed path, and how a truncated partial posted as a
	// finished deliverable. Stop stays the honest answer to "how did the loop
	// end"; Exhausted answers "was it still working when it was told to stop",
	// which is what continuation and rating both actually need. Empty means
	// nothing ran out.
	Exhausted StopReason
	Elapsed   time.Duration
	// Mode names the shape the loop actually ran in, when it ran in one that is
	// not the open loop. It is written so a benchmark can assert that a fold
	// fired rather than inferring it from a turn count that any well-behaved
	// leaf might also have produced. Empty is the ordinary loop.
	Mode string
	// Promote is the executor's explicit verdict that a reflex needs the normal
	// compiled path. Text remains the useful partial discovered before stopping.
	Promote bool
	// SplitRequest is the leaf's own finding that it is holding more than one
	// agent's job, and its account of how the job divides. It is for the
	// settlement path that grows the graph: nil — every leaf that never asked,
	// which is every leaf outside swarm mode — is what keeps that path unentered
	// and this field free.
	//
	// It is separate from Promote because the two say opposite things about the
	// work. Promote says "this is bigger than the shape I was given, run it
	// again properly"; this says "this is several things, and here they are" —
	// the difference between an escalation and a division. Text remains the
	// partial the leaf produced before it stopped, and the parts consume it.
	SplitRequest *SplitRequest
	// ServiceRequests are live ownership leases requested through job.keep.
	// The resident must adopt or stop every lease before settling the leaf.
	ServiceRequests []ServiceRequest

	// Verdict is the same ending seen from the other side. Stop is written for a
	// person reading the run; Verdict is written for whatever learns from it, and
	// the two part company in exactly the case that matters — a leaf that
	// produced text and stopped because it was out of budget reads as "budget"
	// and grades as a failure.
	Verdict provider.Reading

	// Ran is the tail of what the leaf actually did: the last calls it made,
	// in order, with the arguments clipped. ToolCalls already counted them and
	// a count settles nothing — the question a reader of a finished job
	// actually has is whether the check the deliverable claims to have run
	// appears anywhere in the run. The trace file answers that too, but it is
	// a file in the workspace holding every turn's prose; this is the same
	// evidence in memory, bounded, and already beside the text it is used to
	// check. It is a tail and not a transcript: absence in it is evidence, not
	// proof, and whatever reads it must say so.
	Ran []string
	// Commands is the bounded tail of shell commands this leaf issued itself,
	// in the order it issued them, with each command clipped so a pasted heredoc
	// cannot ride into a judge's context. Ran cannot answer this question: it is
	// a tail of ALL calls, so forty edits can displace a check that ran first,
	// and parsing its JSON here would create a second answer to the question the
	// shell tool already answers.
	Commands []string
	// CommandsRun is how many shell commands this leaf issued in total. It says
	// when Commands was cut, so a bounded list can never read as the whole run.
	CommandsRun int
	// Standing is what this leaf's own closing photograph found and the leaf
	// still held when it landed. It is empty after the leaf settles its finding
	// and whenever that reading found nothing.
	Standing []SelfCloseFinding

	// Baseline is what was already broken before this work began: the checks
	// that came back red, and were red in exactly the same places before the
	// leaf touched the workspace.
	//
	// It exists because the delivery gate was reading a suite's absolute state
	// as a verdict on the change, and a repository with one pre-existing red
	// test therefore convicted every correct patch that passed through it — a
	// measured, repeated way of throwing finished work away (audit-notes
	// §14.4.1). A worker that can tell the difference owes the judge the
	// difference in words, because the judge cannot rerun anything. Only a
	// worker that actually photographs the repository before it starts fills
	// this in; every other leaf leaves it empty, which reads as "no claim".
	Baseline []string

	// Regressed names the project's own checks that passed before this work and
	// fail after it.
	//
	// It exists because a leaf's own new tests are the ONE signal that
	// structurally cannot see a regression: the leaf wrote them, so they test
	// what the leaf was thinking about and nothing else. Three graded runs in
	// the 2026-08-28 sweep shipped patches that deleted attributes their
	// repositories already had, every hidden test failed on setup, and from
	// inside the run there was no signal at all — one of them ran its own tests
	// thirty-three times and scored zero. Test COUNT correlates with nothing.
	// Two measured runs of the project's own command, before and after, are the
	// only thing that can see it, and that is what fills this in.
	//
	// NIL MEANS NO CLAIM — nobody looked, because the project declares no
	// verification entrypoint, or the leaf's wall could not afford the reading,
	// or the command would not run, or the tree never changed. Empty-non-nil is
	// not a distinction anything downstream needs and nothing writes one: this
	// field is nil or it is populated. A populated one is a finding the gate
	// raises itself, with no citation to weigh, because the person never had to
	// ask for their repository to keep working (docs/design/gate/SETTLEMENT.md
	// §4, FAILSAFE.md clause 2).
	Regressed []string

	// OwnFailing names the red checks that FIRST APPEARED AFTER THE BASELINE:
	// the ones this run wrote itself, and did not get passing.
	//
	// It is what Regressed used to swallow. A leaf whose own new checks are red
	// has not finished; a leaf that turned somebody else's check red has broken
	// the repository, and calling the first one the second is how happy-dom's
	// nemotron n1 run was failed for breaking checks it had written that hour
	// while the grader scored the same tree 9 of 9.
	//
	// Nil on every worker that cannot take two readings of the tree, which reads
	// as no claim.
	OwnFailing []string

	// Removed names the PUBLIC names this work deleted: a name the tree spelled
	// before the job's first change that the finished tree does not, in the
	// files the run's own record says it changed.
	//
	// It is Regressed's other half and it is the half a suite cannot see. A
	// check proves something is exercised; it never proves nothing else exists.
	// igel s11 deleted eight public class attributes off `Igel` and its own
	// reading of the finished tree came back BETTER — named 2 → 14, red 2 → 0 —
	// while all twenty-four hidden tests failed at setup on `Igel.results_path`.
	//
	// Nil on every worker that cannot take two readings of the tree, which reads
	// as no claim and never as nothing removed. See verify.Surface.
	Removed []string

	// Unbound names what this work READS that nothing in the tree binds: an
	// attribute a class reaches for and no code assigns, a binding an import
	// asks an in-tree module for that the module does not define.
	//
	// It is the question one step further back than Removed. That one compares
	// two readings and reports a name that USED to be there; this needs only the
	// tree as it stands, and it is the one measurement a run gets on a project
	// with no baseline at all. igel s14 imported `temp_post_req_data_path` from
	// a module it had just stopped binding it in, all twenty-four hidden tests
	// failed on `ImportError`, and the only thing the run could say was that its
	// own checks were red. See verify.UnboundReferences.
	//
	// Nil on every worker with no workspace to read, which reads as no claim.
	Unbound []string

	// Verification is the whole photograph the two readings above came out of:
	// the entrypoint that was run, the budget it was run on, and both readings'
	// complete rosters rather than only their red halves.
	//
	// Regressed is one subtraction over it. The delivery gate needs the other
	// two. WHICH CHECKS EXIST answers whether anything at all exercises a
	// behaviour the request asked for, and WHICH CHECKS STOPPED EXISTING is the
	// one thing that catches a worker deleting the test that was failing it —
	// neither question can be asked of a list of failures. The Taken flag is
	// what keeps the gate honest about a project that declares no verification:
	// nobody looked and nothing may be concluded, which is a different fact from
	// a suite that came back clean. See docs/design/gate/ACCEPTANCE.md.
	//
	// A zero value is a photograph nobody took, which is what every worker that
	// cannot take two readings leaves here.
	Verification verify.Reading

	// Account is the worker's structured account of the work itself: the files
	// it changed, the commands it issued itself, and the checks the closing
	// photograph ran with what each one found. See [Account] for why a leaf that
	// reports only prose is expensive.
	//
	// It is a pointer and it is usually nil. Only a worker that can observe its
	// own change set and run its own verifier has anything to put here; every
	// other leaf leaves it unset, which reads as "no claim" — the same silence
	// Baseline uses, and for the same reason.
	Account *Account

	// Calibration is what the worker noticed about its own fit for this job:
	// free-text sentences, in the worker's own voice, about whether the work sat
	// comfortably inside its envelope, under it, or at the top of it.
	//
	// It is deliberately prose rather than a number or an enum. The only reader
	// is the recalibration call that rewrites a subharness's three anchor
	// examples, and that reader is a model reading evidence — a "fit: 0.3" would
	// have to be invented at one end and interpreted at the other, and both
	// halves would be fiction. Nothing branches on it and nothing may; the
	// generalist emits none of it, so every existing profile record and every
	// existing prompt is exactly what it was.
	//
	// A worker writes these about ITSELF. "This sat under my envelope" is a fact
	// this executor is uniquely placed to observe; "the other worker should have
	// had it" is a judgement it is not, and the note says the first thing.
	Calibration []string
}

Outcome is what came back.

Text is the deliverable and is what flows into dependents. Artifacts are files left in the workspace; they are referenced by path rather than pasted, so a large output never lands in three dependents' contexts at once.

func (*Outcome) Calibrate

func (o *Outcome) Calibrate(note string)

Calibrate appends one self-observation, ignoring the empty ones so a caller can compose a note conditionally without guarding every call.

func (*Outcome) Overran

func (o *Outcome) Overran() bool

Overran reports that the leaf still had work in hand when its resources ran out — the condition the continuation subsystem exists for. It reads both fields because a leaf can arrive here two ways: cut off outright (Stop), or told to land and complying (Exhausted). Only the resource endings count; a deadline is a fact about the clock rather than about work left undone, and a user pause or cancel is a decision rather than an overrun.

A judged hand-back counts. The straggler was still working when it was told to land — that is the entire finding against it — so the question "is there work left here" is exactly the question the continuation subsystem exists to answer about it, and answering it is the "divide" arm of the judgement the hand-back was made to reach.

func (*Outcome) String

func (o *Outcome) String() string

type Provenance

type Provenance string

Provenance is where a subharness came from, in the three words a person reads. They are person-facing strings rather than codes because they are drawn literally — there is no second table translating a constant into English, and so no way for the two to drift apart.

const (
	// FromBinary is a subharness compiled into this build: the defaults the
	// owner ships, always present, never missing on a fresh machine.
	FromBinary Provenance = "built-in"
	// FromYou is a bundle out of the person's own home store.
	FromYou Provenance = "yours"
	// FromProject is a bundle committed into the repository they are working in,
	// which is the whole of the org-sharing story: sharing is a pull, review is a
	// pull request, and history is the versions.
	FromProject Provenance = "from this project"
)

type Range

type Range struct {
	Base, Head string
}

Range is the span of repository history one node's work occupies: the commit its own change set is measured from, and the commit it is measured to.

It exists because the two accounts of what a coding leaf changed were not the same account. One was narration — the file rows the engine published on the wire as each tool call finished — and it was per-PASS: a second attempt at the same node reported only what the second attempt touched, so a repair round that rewrote prose over a landed diff reported a change set of nothing. The other was git: a before/after read of the shared workspace, keyed by leaf and therefore correct across every pass. The second one is the truth, and this is the handle that lets it be stated rather than recomputed differently by each reader.

Base is durable per NODE and not per pass. It is written to a git ref the first time the node opens a view and read back on every later one, so it survives a process restart with no in-memory carry at all — which is the only way an account of "what this node changed" can outlive the run that made it.

func (Range) Derived

func (r Range) Derived() bool

Derived reports that this range was actually measured. An empty base or head is a range nobody could compute, and it must not read as "measured, and the answer was nothing".

type Registry

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

Registry picks an executor by subharness. Nearly every node carries none and resolves to the general loop; the lookup is what makes adding a specialised worker a registration rather than a change to the scheduler.

IT IS ALSO THE SUBHARNESS REGISTRY, and there is deliberately only the one. The name→worker lookup with a fallback that this type has always been is exactly the shape a typed subharness needs, so the subharness contract GREW this rather than standing a second registry beside it (docs/SUBHARNESS-PRD.md §3). The two halves answer two different questions about the same names:

  • executors/fallback below serve a LEAF that named a worker. Registry.For degrades to the generalist for an unknown name, because a plan that asked for a worker this build does not have should still get its work done.
  • runners/bundles serve a PERSON or a program that named a subharness. Registry.Subharness refuses an unknown name, because typing one that does not exist and quietly getting a different one is worse than being told. See runner.go for the lookup order across the layers.

func NewRegistry

func NewRegistry(fallback Executor) *Registry

func (*Registry) For

func (r *Registry) For(subharness string) Executor

For returns the executor for a subharness, falling back to the general one. An unknown name is served rather than refused: a plan that asks for a worker we do not have should still get its work done by the generalist.

func (*Registry) Generalist

func (r *Registry) Generalist() Executor

Generalist is the worker a run falls back to. It is the same executor Registry.For hands an unknown name, exposed by name because the deoptimization path needs it deliberately rather than by accident: a program whose guard did not pass has ASKED for the long way, and the long way is this.

func (*Registry) Manifests

func (r *Registry) Manifests() []Manifest

Manifests is every subharness this registry can reach, compiled-in and stored, in one list with the lookup's own precedence already applied: a name found at two layers appears once, described by the layer that would actually run it.

This is the list every surface draws — the `/subharness` picker, the catalog index when it arrives, the per-turn reminder. There is no second enumeration anywhere, which is what makes "the person cannot tell which is which" true by construction rather than by discipline.

func (*Registry) Register

func (r *Registry) Register(executor Executor)

Register adds a specialised executor.

func (*Registry) RegisterRunner

func (r *Registry) RegisterRunner(runner Runner) error

RegisterRunner adds a Go-native subharness to this registry.

IT IS PER-REGISTRY AND NOT PROCESS-GLOBAL, and the split is the same one this package has always drawn: a DESCRIPTION is a fact about the process and lives in the global table (subharness.go), while a WORKER is built by a surface out of its own clients and workspaces and lives on the registry that surface built. A JS runner needs no such wiring and could have been global; making it follow the same rule is what keeps one lookup instead of two.

A runner whose manifest does not validate is refused, with the manifest's own prose. Registration is the one door, so a subharness is never half-installed.

func (*Registry) Subharness

func (r *Registry) Subharness(name string) (Runner, error)

Subharness resolves a name to what will actually run it, in the lookup order PRD §7 states: compiled-in first, then the packed trailer, then the project store, then the home store; first hit wins.

COMPILED-IN IS LAYER ZERO AND WINS, which is this build's reading of "Go-native subharnesses are layer 0 implicitly". A bundle on disk may not shadow a name the binary ships, because those names are what the manual and the system prompt describe — a store that could quietly replace one would turn both into documents about a program that did not run.

A name nothing has is ErrNoSubharness. That is the difference between this door and Registry.For beside it: For serves a leaf that asked for a worker this build does not have, because the work still has to get done; this serves a person or a program that named a subharness, where getting something else would be worse than being told.

func (*Registry) UseBundles

func (r *Registry) UseBundles(layer Layer, source BundleSource)

UseBundles puts a store in front of this registry at one layer. The store lane registers one source for the project store and one for the home store; the phase that builds the packed trailer registers one at LayerPacked and edits no lookup.

Registering the same layer twice replaces it, because a layer is a PLACE and a place has one store.

type Result

type Result struct {
	Content string
	IsError bool

	// Followup carries multimodal content that must reach the next model turn.
	// The ordinary text result is still emitted first so tool-call pairing
	// remains valid on every OpenAI-compatible backend.
	Followup []ai.ContentPart
	Usage    Usage
	// contains filtered or unexported fields
}

Result is one tool's answer. A failure is a Result, never a Go error: the model has to see what went wrong to fix it, and aborting the loop over a mistyped path throws away every turn that came before.

type RunResult

type RunResult struct {
	// Output is the typed answer, matching the manifest's output schema. A run
	// that ends with nothing here has not finished, and [RunResult.Finished] says
	// so without anybody having to remember the rule.
	Output json.RawMessage `json:"output,omitempty"`

	// Report is the short prose a person reads: what happened, in the
	// vocabulary law's words. It is not a summary of the output — the output is
	// rendered from its own schema — it is the account of the run.
	Report string `json:"report,omitempty"`

	// Artifacts are the files the run made, if it made any.
	Artifacts []Artifact `json:"artifacts,omitempty"`

	// Incomplete is WHY it did not finish, in a person's words, and empty when
	// it did. Out of budget, out of time, a question nobody was there to answer,
	// a step that could not produce its shape: all of them end here, all of them
	// say what ran out. "incomplete" is one of the five sanctioned words for the
	// state of work, and nothing may reach a person calling this a failure.
	Incomplete string `json:"incomplete,omitempty"`

	// FellBack is set when the work was done the long way — the program's guards
	// did not pass, or it broke partway, and the general worker finished the job
	// with the original input. IT IS NOT A FAILURE AND MUST NOT BE DRAWN AS ONE:
	// the person is told the step needed a closer look and was handled the long
	// way. The value is the person-facing half of that sentence, drawn from the
	// guard's own Because line where it had one.
	FellBack string `json:"fell_back,omitempty"`

	// Spend is the whole run's ledger, which is the sum of its journal's
	// entries and can be nothing else.
	Spend Spend `json:"spend,omitzero"`
}

RunResult is what a run produced.

func Deopt

func Deopt(ctx context.Context, registry *Registry, manifest Manifest, input json.RawMessage, env Env, because string) (RunResult, error)

Deopt does the job the long way and answers what the generalist produced.

THE INPUT IS THE ORIGINAL INPUT, unchanged. A fallback that reshaped what it was given would be a third worker nobody registered — and the whole promise of the long way is that it is the job as it stood before any program touched it.

The result carries the fell-back sentence forward, so a run that was handled this way says so wherever it is read afterwards, whatever the generalist made of it. A generalist that itself could not be reached is an error rather than a silent nothing: there is no fourth worker under this one.

THE MANIFEST IS THE PROGRAM'S OWN, NOT THE GENERALIST'S, and it is here for one reason: it carries the ceiling somebody approved. DeoptHeld states the whole argument. A run whose ceiling does not reach the long way stops incomplete, in the person's own register, rather than quietly becoming a wider agent than anybody said yes to — and that ending is produced HERE, in the one function both surfaces reach, so neither can skip it.

func (RunResult) Finished

func (r RunResult) Finished() bool

Finished says the run produced what it promised. It is the machine-checkable finish line, asked in one place so that no surface invents a second reading of what "done" means.

type Runner

type Runner interface {
	// Manifest is everything true about this subharness before it runs. It
	// answers the name too, which is why there is no second Name method: a
	// runner whose name and whose manifest could disagree is a registry entry
	// filed under something it does not describe.
	Manifest() Manifest

	// Run does the work. The input is JSON matching the manifest's input schema;
	// the Env is the whole capability surface (env.go) and the only way to
	// spend. AN ERROR IS A RUN THAT COULD NOT BE MADE TO HAPPEN — a broken
	// bundle, a dead context — and is what the deoptimization path catches. A
	// run that HAPPENED and did not finish returns a [RunResult] with its
	// Incomplete reason set and no error at all.
	Run(ctx context.Context, input json.RawMessage, env Env) (RunResult, error)
}

THE RUNNER: a subharness is a FUNCTION, not a chat.

That sentence is PRD §4 and it is the keystone every downstream feature leans on. A run takes typed input, produces typed output, and ends either having produced its declared shape or INCOMPLETE with a reason. There is no third ending, and in particular there is no "done with a strange message" — which was the only ending the old one-string program form could offer, and the reason nothing downstream of a run could ever be built on it.

Two implementations are real and the contract permits no more than it has to:

  • A GO RUNNER implements this natively and is registered at compile time. The owner's custom Go subharnesses land here first-class with zero porting, and the worker itself is re-fronted through it (ExecutorRunner).
  • THE JS RUNNER IS ONE GENERIC GO RUNNER parameterized by a bundle. There is not one runner per bundle: there is one goja host, and a bundle is its argument. The runtime lane builds it; this file defines the door it comes through (BundleSource).

Future runners — a subprocess, WASM — are permitted by this shape and are not being built. Nothing here may be complicated by their possibility.

type Scheduler

type Scheduler struct {

	// Budget bounds the whole run's token spend, in prompt+completion tokens.
	// Zero means unbounded — each leaf still has its own budget. When the
	// cumulative spend passes it, no new node is launched; whatever is in
	// flight lands (mirroring the per-leaf landing reserve), never-started
	// nodes are marked, and Run reports the stop reason.
	Budget int

	// Escalations is how many times a failed leaf may be re-run on a stronger
	// model. Zero — the default — is exactly today's behaviour: a leaf that
	// fails, fails. It is only worth setting when there is somewhere stronger to
	// go, so the caller sets it from the panel rather than the scheduler
	// assuming one exists.
	//
	// Only verdicts that a better model could plausibly fix count: running out
	// of budget, running out of turns, returning nothing at all. A provider
	// failure is weather and a rate limit is not cured by spending more.
	Escalations int

	// NodeTimeout is the watchdog on a single node. The executor has its own
	// deadline, so this only fires when an executor is wedged past every
	// deadline it was given — a hung pipe, a stuck transport. The ending is
	// recorded as an [Abandoned] rather than letting one stuck goroutine freeze
	// the run silently and forever, and what that ending MEANS is decided in
	// [Scheduler.apply]: a node with a record goes back on the queue, and a node
	// that reached nothing is failed. Zero disables it.
	NodeTimeout time.Duration

	// BeforeLaunch applies process policy immediately before a leaf starts.
	// Returning an error stops new launches while already-running leaves land.
	BeforeLaunch func(context.Context) error

	// OnEvent reports state changes as they happen. A run is long and mostly
	// invisible; without this the only feedback is silence followed by a graph.
	OnEvent func(Event)
	// contains filtered or unexported fields
}

Scheduler drives a graph to completion.

It is dependency-driven rather than wave-locked. Waves are a way to describe a schedule, not a way to run one: waiting for the slowest node of a wave before starting anything in the next holds back work whose inputs are already finished. A node starts the moment the specific nodes it named are done, which is what the whole binding pass was for.

func NewScheduler

func NewScheduler(registry *Registry, workspace *Workspace, concurrency int) *Scheduler

func (*Scheduler) Run

func (s *Scheduler) Run(ctx context.Context, graph *plan.Graph) error

Run executes every runnable node in the graph and records results onto it.

The graph is mutated in place and never concurrently: workers return outcomes through a channel and the scheduler applies them on its own goroutine. That keeps the one shared structure single-writer, which matters more here than anywhere else — appending to g.Nodes while a worker held a *Node has already cost us a duplicated subtree once.

func (*Scheduler) Usage

func (s *Scheduler) Usage() Usage

Usage is the total cost of the run, summed as outcomes are applied. It is written on the scheduler goroutine. Media spend gates may read it from a worker immediately before generation, so the small value is copied under a read lock.

func (*Scheduler) WithGovernor

func (s *Scheduler) WithGovernor(governor *Governor) *Scheduler

WithGovernor replaces the shared host gate. Production uses the process-wide one so a headless run and a resident runner in the same process read the same machine rather than each discovering its own number.

type Schema

type Schema json.RawMessage

Schema is a JSON Schema, carried as the bytes it was written as.

IT IS THE BYTES AND NOT A GO STRUCT, and that is the language-agnostic half of the contract doing its job: a bundle's manifest.json round-trips through this field unchanged, a Go runner's reflected schema arrives as the same bytes, and nothing in this tree has to own a model of JSON Schema in order for either to be stored. Schema.Fields reads out the little the intake card actually needs and is deliberately shallow — the card draws a form, it does not validate a document.

func (Schema) Empty

func (s Schema) Empty() bool

Empty reports that nothing was declared. It is a fact and not a fault: a subharness that takes no input is a real thing, and the intake card for one has nothing to ask and launches straight away.

func (Schema) Fields

func (s Schema) Fields() []Field

Fields reads the schema's named fields out, in a stable order.

THE ORDER IS `x-order` WHERE THE SCHEMA STATES ONE AND ALPHABETICAL OTHERWISE, because Go decodes an object into a map and a card whose fields moved between two draws of the same subharness would be a card nobody could learn. A schema that cares about the order of its own form says so; one that does not gets an order that is at least the same every time.

func (Schema) MarshalJSON

func (s Schema) MarshalJSON() ([]byte, error)

MarshalJSON writes the schema as itself. A schema nobody declared is `null`, which is what an omitted field already means, so a manifest with no input schema and a manifest whose input schema is absent are one document.

func (*Schema) UnmarshalJSON

func (s *Schema) UnmarshalJSON(data []byte) error

UnmarshalJSON keeps a copy of the bytes. The copy matters: the decoder's buffer is reused, and a schema that aliased it would change under a manifest that had already been read.

func (Schema) Validate

func (s Schema) Validate() error

Validate says whether this is a schema at all, in prose. It checks that the bytes are JSON and that they describe an OBJECT, because every door on both sides — the intake card's fields, the adapter that fills them from an upstream task, the output the "done" surface renders — is written against named fields and has nothing to draw for a bare string.

type SelfCloseFinding

type SelfCloseFinding struct {
	// Kind is one of the four constants above.
	Kind string
	// Names is what the finding is ABOUT, in the reading's own words. A kind
	// alone sends a worker off to run a suite; the name is the diagnosis.
	Names []string
	// Fact is the finding without an instruction addressed to a worker that may
	// already have stopped. A handover needs this clause on its own, while
	// Sentence composes it with the move offered to a leaf still standing.
	Fact string
	// Sentence is the finding put to the leaf in the person-facing register: a
	// fact, then the one move that settles it, then the honest alternative —
	// because a measurement can be right about the tree and wrong about the
	// request, and a leaf told to obey a measurement it disagrees with will
	// invent work rather than say so.
	Sentence string
}

SelfCloseFinding is one fault a leaf's closing photograph raised against the leaf's own work: what kind it is, what it is about, and the sentence the leaf is handed.

func SelfCloseFindings

func SelfCloseFindings(outcome *Outcome) []SelfCloseFinding

SelfCloseFindings reads the leaf's own after-photograph for faults the LEAF caused.

Every one of these fields is already scoped to the run's own record of what it changed (see photograph.go), so a finding here is a finding about this work and never about the repository it arrived in. A nil field is NO CLAIM — nobody looked — and reads here exactly as it reads at the gate: silence, never a clean bill.

type SelfCloseRoom

type SelfCloseRoom struct {
	// Turns, Tokens and Wall are what is left on each meter that BOUNDS this
	// belt. A belt that does not meter one of them leaves it at zero and says
	// so through the flag below rather than reading as exhausted — a belt with
	// no turn cap and no token ceiling reads zero on both, and reading that as
	// "out of turns" would silently switch the mechanism off for a whole belt.
	Turns  int
	Tokens int
	Wall   time.Duration
	// Left says every meter that bounds this leaf still has something on it.
	Left bool
	// Why is the meter that said no, for the record. Empty when Left.
	Why string
}

func RoomLeft

func RoomLeft(outcome *Outcome, maxTurns, maxTokens int, wall, reserve time.Duration) SelfCloseRoom

RoomLeft subtracts what the leaf has spent from what it was granted.

A NON-POSITIVE GRANT MEANS THIS BELT DOES NOT BOUND THAT, which is a different fact from a grant that has run out, and the two must not be spelled the same way: a belt with no turn cap and no token ceiling reads zero on both, and reading those zeros as "spent" would switch this mechanism off for a whole belt.

THE CLOCK IS THE OTHER WAY ROUND, because a clock is the one meter every belt has and a spent one is the dangerous reading to get wrong. wall is what is LEFT on it, so zero and below mean spent; a belt with no clock at all passes NoWall.

The reserve subtracted from the wall is the leaf's own landing reserve, which is the room already set aside for a leaf to finish safely — precisely the room a close needs, and not one second that was not already the leaf's.

type SelfCloser

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

SelfCloser is one leaf's own closing, held across the leaf's landings so that a kind is put to it once and once only.

It is a value on the belt's stack rather than anything durable, because it is a fact about ONE run of one leaf: a later attempt on the same node is a different worker holding a different transcript, and it is entitled to its own reading of its own work.

func NewSelfCloser

func NewSelfCloser(history *store.Store, task Task) *SelfCloser

NewSelfCloser arms the closing for one leaf. A nil journal is ordinary — a leaf run outside a graph has nothing to write into — and the decision is taken identically with or without one.

func (*SelfCloser) Close

func (c *SelfCloser) Close(outcome *Outcome, room SelfCloseRoom) (string, []SelfCloseFinding)

Close is the whole decision, and it is asked with the after-photograph already on the outcome.

It answers with the note the leaf resumes on and the findings that note is about, or nothing at all when the leaf is to land on the reading it is holding. The findings come back beside the note rather than being re-derived by the caller, because the set that is DUE is not the set the outcome carries — a kind already closed once is standing in both. Both answers are journaled, because "the leaf fixed its own work" and "nobody ever looked" were the same silence in every store this was built from (FAILSAFE.md clause 4).

It never fails a leaf and it never returns an error. A journal that refuses the row, a store that is not there, a task with no node to file against — each changes what is written down and nothing about what the leaf does.

type ServiceRequest

type ServiceRequest struct {
	JobID      int
	Name       string
	Command    string
	Dir        string
	Health     store.ServiceHealth
	LogPath    string
	PID        int
	StartedAt  time.Time
	LeafNodeID string
	// contains filtered or unexported fields
}

ServiceRequest is a live hand-off from a leaf registry to the resident. Its metadata is immutable; Adopt and Stop are idempotent terminal decisions.

func (*ServiceRequest) Adopt

func (request *ServiceRequest) Adopt()

Adopt removes the process from the leaf registry. The existing waiter keeps reaping the shell when it eventually exits; leaf teardown can no longer see or kill it.

func (*ServiceRequest) Stop

func (request *ServiceRequest) Stop()

Stop returns a declined or expired request to the ordinary teardown rule.

type Spend

type Spend struct {
	// Model is what answered, as the PROVIDER reported it, and it is empty when
	// two calls disagreed — a run whose steps pinned models of their own is not
	// a run one name describes, and choosing one of them would be this package
	// stating a fact nobody gave it.
	Model string `json:"model,omitempty"`
	// Calls is every request made, the intermediate rounds of a tool loop
	// included. A call the provider reported no usage for is still counted: a
	// missing count is not a call that did not happen.
	Calls int `json:"calls,omitempty"`
	// Input and Output are the provider's token counts, with cache reads beside
	// the prompt count rather than inside it — the session's own accounting
	// keeps them in exactly this shape.
	Input  int `json:"input,omitempty"`
	Output int `json:"output,omitempty"`
	// CacheRead and CacheWrite are the prompt-cache accounting.
	CacheRead  int `json:"cache_read,omitempty"`
	CacheWrite int `json:"cache_write,omitempty"`
	// CostUSD is the provider's OWN figure, summed. Zero is "the provider did not
	// say", which is not the same fact as free — and under the emptiness law a
	// surface that cannot tell them apart draws nothing rather than $0.00.
	CostUSD float64 `json:"cost_usd,omitempty"`
	// contains filtered or unexported fields
}

Spend is what one call, or one whole run, cost.

THE FIGURES ARE THE LEDGER'S, field for field: internal/subharness/usage.go carries exactly these — the model the provider named, the call count, the two token counts, the two cache counts, and the provider's own dollar figure — and the session folds them through [Agent.foldHarnessUsage] into the auxiliary door. Reading them here in the same spelling is what keeps one run from having two accounts of itself.

It is a type in this package rather than that one for a reason worth stating, because it is the one place this contract does not simply reuse what exists: the ledger's fold methods are unexported, so nothing outside internal/ subharness can add a call to one. The goja host and every Go runner live outside it. So the fields are the ledger's and the doors are exported, and the session's fold takes either.

NOTHING HERE LOCKS, which is the same law the ledger states about itself and for the same reason: host calls are made from the one goroutine a run has. A runtime that ever ran them concurrently owns this type's safety along with everything else it changed about running a program.

func (*Spend) Add

func (s *Spend) Add(other Spend)

Add folds one model call in. A nil receiver is a caller that is not counting, which is every guard check and every runner that spends nothing.

func (Spend) Mixed

func (s Spend) Mixed() bool

Mixed reports that this run's calls did not agree on a model, which is a different fact from having no model at all. A surface that wants to say "on several models" rather than nothing asks this.

func (Spend) Reported

func (s Spend) Reported() bool

Reported says whether the provider gave this spend any accounting at all. It is the question asked before billing anybody: ten calls to an endpoint that publishes no usage leave every field zero, and folding that into a session total would be this build claiming the work was free.

type SplitPart

type SplitPart struct {
	Title   string
	Summary string
	Brief   string
}

SplitPart is one of the jobs a leaf found inside its own assignment.

Title is what the part is called where a person reads the graph. Summary is the one line that tells a planner what this part is FOR, so a division can be weighed against its siblings without opening any of them. Brief is the part's whole assignment, written to be handed to an agent that will read nothing else — which is the ownable-subject test in structural form: a brief that has to say "after the previous part finishes" is describing a phase, and a phase is not a part.

type SplitRequest

type SplitRequest struct {
	// Parts are the jobs the leaf believes it is holding, in no particular
	// order — a division exists to be run at the same time, and parts that have
	// an order are a sequence somebody has mislabelled.
	Parts []SplitPart
	// Evidence is what the leaf actually read or discovered that revealed the
	// division: the file it opened, the count it found, the shape of the thing
	// in front of it. It is here because a division decided from the assignment
	// alone is the division the build already made and refused; what makes this
	// one worth a round is that it was made against what is really there, and a
	// planner handed the request without the finding cannot tell the two apart.
	Evidence string
}

SplitRequest is a leaf saying, in the middle of its own run, that it is holding more than one agent's job.

Until this existed a node could only grow the graph by failing first: it spent its whole budget, settled overrun, and the remainder was replanned afterwards — the same decision this carries, bought at the price of a wasted leaf. So the cheap version is the one the worker asks for, and the expensive one stays as the backstop for the leaf that never noticed.

It is a request and not an instruction. What is done with it is the settlement path's business and it goes through the same governor every other way of growing a running job goes through, so a leaf that asks to divide into nine gets whatever the caps and the planner between them allow — including nothing, which delivers the partial exactly as a refused overrun does.

func (*SplitRequest) Valid

func (r *SplitRequest) Valid() bool

Valid reports whether a request is one the growth path may act on: two or more parts, each of which names itself and carries an assignment.

A one-part "division" is the split that only restates, which plan.WorthKeeping would refuse a round later at the price of a planning call — so it is refused here, for free. A part with no brief is a title with nothing behind it, and handing an agent one is how a node comes to be executed against its own name.

It is nil-safe because every caller reaches it through a field that is nil on every leaf that never asked.

type StagedAttachments

type StagedAttachments struct {
	// Documents and Images are workspace-relative, which is the spelling the
	// leaf's own tools take: read_document and view_image both address the
	// workspace.
	Documents []string
	Images    []string
	// ImageFiles are the same images as absolute paths, for the turn content a
	// model with eyes receives directly.
	ImageFiles []string
}

StagedAttachments is what one job's workspace holds after staging.

func StageAttachments

func StageAttachments(space *Workspace, root string, attachments []string) (StagedAttachments, error)

StageAttachments materializes a node's attachments as immutable workspace inputs. References resolve out of the store; a path recorded before copies existed still resolves from disk, so old work keeps running.

type StopReason

type StopReason string

StopReason says how the loop ended. It is recorded rather than inferred because "the model finished" and "we cut it off" produce identical-looking output and mean opposite things about whether the result can be trusted.

const (
	StopDone      StopReason = "done"      // the model stopped asking for tools
	StopTurnCap   StopReason = "turn-cap"  // ran out of iterations; a runaway backstop
	StopBudget    StopReason = "budget"    // ran out of tokens; the leaf was too expensive
	StopDeadline  StopReason = "deadline"  // ran out of wall clock
	StopError     StopReason = "error"     // the provider failed in a way we could not absorb
	StopPromote   StopReason = "promote"   // a reflex discovered that it is a job
	StopPaused    StopReason = "paused"    // user hold observed between turns
	StopCancelled StopReason = "cancelled" // user cancellation observed between turns

	// StopEmpty is the runaway-reasoning circuit breaker: a turn that spent most
	// of what the leaf had left and returned no visible text at all. It is
	// separate from StopError because nothing failed — the call succeeded and
	// was paid for in full — and separate from StopDone because nothing was
	// produced.
	StopEmpty StopReason = "empty"

	// StopOverrun is a leaf that was landed for running far past what work of
	// its kind has ever cost on this machine. Nothing writes it any more; it is
	// kept because a leaf run recorded before it was retired still carries the
	// word on disk (internal/store/leafrun.go), and a reason a reader cannot
	// name is worse than one nothing produces. It is separate from StopBudget
	// because nothing ran out, and from StopError because nothing failed.
	StopOverrun StopReason = "overrun"

	// StopSplit is the cooperative ending: the leaf found mid-work that it was
	// holding several agents' jobs, said what they were, and stopped so they
	// could run instead of it.
	//
	// It is its own reason rather than one of the endings above because every
	// one of those would misreport it. StopDone says the work is finished and
	// it is not. StopPromote says the shape was wrong and the same work should
	// be run again properly, which is an escalation and not a division.
	// StopBudget and StopOverrun say a resource ran out, and nothing did — the
	// grant was untouched and the leaf gave it back.
	//
	// It deliberately does NOT make Outcome.Overran true. Overran is the
	// question "was there work left when the resources ran out", and the
	// continuation subsystem it gates exists to replan a remainder from an
	// exhaustion. A cooperative split has its own path with its own reason in
	// the growth journal, and letting both fire on one settlement would spend
	// two rounds on one decision.
	StopSplit StopReason = "split"
)
const StopNoProgress StopReason = "no-progress"

StopNoProgress is the reason recorded when the guard terminates a leaf that was not making forward progress. It is separate from StopBudget and StopTurnCap because it is a different finding: the leaf had money and turns left, and was spending both without advancing. Grading it as a budget stop would teach the ruler nothing it does not already learn from the budget; the distinct reason is what lets the recalibration path see that the node was sized correctly and the model was the thing that stalled.

const StopToolTimeouts StopReason = "tool-timeouts"

StopToolTimeouts is the reason recorded when one command has timed out often enough that running it again would repeat a fact the leaf already knows.

func (StopReason) OutOfRoom

func (s StopReason) OutOfRoom() bool

OutOfRoom reports that this ending is a leaf THAT WAS STILL WORKING when something it could not argue with stopped it: its tokens, its turns, or its clock.

It is a method on the reason rather than a rule at each reader because the answer travels: the settlement asks it of an Outcome, and the scheduler asks it of an ExecResult that holds nothing but this string. Two spellings of one question is how a leaf came to be judged done on one side of a seam and cut off on the other.

The empty reason answers false, and every caller that carries this across a seam says separately whether an ending was recorded at all — a StopReason is a string, and its zero value must never be readable as "it finished fine".

type SubharnessInfo

type SubharnessInfo struct {
	Name    string `json:"name"`
	Purpose string `json:"purpose,omitempty"`
	// PriorAnchors is the capacity ruler in the style of plan/size.go's three
	// worked examples: comfortably atomic, borderline, oversized. A saved
	// program that measures differently from the generalist says so here.
	PriorAnchors string `json:"prior_anchors,omitempty"`

	// DeadlineFloor and the scaling pair are the budget shape. A leaf's hang
	// backstop is not a policy about patience, it is a claim about how long
	// this kind of work legitimately takes. Zero values fall back to the
	// generalist's shape, so a registration that says nothing about time is
	// served rather than refused.
	DeadlineFloor     time.Duration `json:"deadline_floor,omitempty"`
	DeadlineStep      time.Duration `json:"deadline_step,omitempty"`
	DeadlinePerTokens int           `json:"deadline_per_tokens,omitempty"`
}

SubharnessInfo is one registration: what it is for, and how much room its work is given.

THE TAGS ARE THE ON-DISK SPELLING OF HALF A MANIFEST. Manifest embeds this struct and is the document a bundle's manifest.json actually is (PRD §6) — a file a person writes by hand, a model iterates on, and a pull request reviews. Untagged, this half of it would come out spelled in Go field names next to the tagged half's lowercase ones, and the format would be two conventions in one object. They are named the way the fields beside them are, and everything but the name is omitempty, so a manifest that says nothing about its budget shape carries nothing about it.

func SubharnessFor

func SubharnessFor(string) SubharnessInfo

SubharnessFor resolves a name to what will actually run it, which is the one worker. An unknown or empty name is it rather than an error — the same degradation Registry.For promises, said one layer up so a budget shape can be read before dispatch.

func (SubharnessInfo) Deadline

func (s SubharnessInfo) Deadline(budgetTokens int) time.Duration

Deadline is the budget shape applied to one leaf's token grant.

THIS IS THE ONLY PLACE IN THE PROCESS THAT DOES THIS ARITHMETIC, and that is the law rather than a tidiness. The same fifteen-minute floor and the same minute per fifty thousand tokens were written out longhand in four places — the chat surface, the headless runner, the linear loop's own fallback and the claim reaper's window — and the four then had to be kept in step by hand across a change none of them could see. The reaper's window is derived from this figure two additions along, so a floor that moved here and nowhere else put the backstop BELOW the deadline it is meant to sit above, which is not a backstop but the thing that fires first. `TestOnlyTheSubharnessTableSizesALeafsRoom` fails the build on a fifth copy.

func (SubharnessInfo) DeadlineWithin

func (s SubharnessInfo) DeadlineWithin(budgetTokens int, remaining time.Duration) time.Duration

DeadlineWithin widens one leaf's token-sized room to use the wall it runs under, while leaving the watchdog's landing pad inside that wall.

A forty-five-minute errand handed its only leaf the generalist's fifteen- minute floor, with no path by which the leaf could learn that another thirty minutes were going unused. THE WALL IS A CEILING AND THE TOKEN GRANT IS A FLOOR: this method only widens. A short wall never takes away room the token grant already bought, and no wall leaves the shape byte-for-byte unchanged.

func (SubharnessInfo) Watchdog

func (s SubharnessInfo) Watchdog(budgetTokens int) time.Duration

Watchdog is the node watchdog above one leaf's token grant: its own deadline plus the landing pad. The executor has a deadline of its own, so this only fires when a leaf is wedged past every limit it was given.

type Task

type Task struct {
	NodeID int
	// NodeKey is the identity everything this leaf writes is filed under: its
	// artifact bucket, its flight recorder, its spilled observations, its
	// background job logs.
	//
	// It exists because NodeID is not always unique. A headless run's NodeID is
	// its plan node's number, which is unique within the graph — that path sets
	// nothing here and keeps the numeric spelling it has always written. The
	// resident surface has no such number: it holds a store node, whose creation
	// sequence belongs to the whole splice, so a four-part job handed four
	// workers one bucket, one recorder and one set of spill names. Siblings run
	// concurrently by construction, so that is not a naming inelegance — it is
	// one worker's spilled observation overwritten by another's while a stub in
	// its context still points at the file.
	//
	// Empty falls back to NodeID, which is what keeps every existing headless
	// path byte-identical. See [Task.leafKey].
	NodeKey string
	// StoreNodeID is the durable provenance anchor used when a background job
	// requests promotion. One-shot execution leaves it empty.
	StoreNodeID string
	Title       string
	Goal        string // the whole plan's goal, for orientation
	Brief       string // the self-contained instruction: what the job is
	Contract    string // the working method: how this kind of job is done well
	// Spec is the same job as the object the planner authored, carried whole.
	// Brief and Contract above are two of its fields and remain what this
	// executor reads; the object is here for the worker that can be handed a
	// spec directly instead of prose reassembled at the boundary. An empty
	// Spec renders to the empty string and changes nothing.
	Spec   plan.Spec
	Inputs []Input
	// OutputHint is where a file goes if this work needs one. It is an
	// address, never an instruction: what a leaf owes is its final message,
	// and a path offered as though a document were expected is how a job came
	// to leave 07-pr-482-code-review.md, 70-read-diff.md and 144-synthesis.md
	// in a person's own directory.
	OutputHint string
	// Intermediate says this leaf's result is consumed by later work rather
	// than read by the person who asked. Its handoff is its final message, so
	// it is offered no deliverable path at all — OutputHint, when it carries
	// anything, names the run's own scratch.
	Intermediate bool
	// ImagePaths are user-supplied inputs attached to the initial leaf turn.
	ImagePaths []string
	// DocumentPaths are user-supplied documents already staged in the
	// workspace. The brief names them; this is the same fact in structural
	// form, and it is what arms the document reader before turn 1 instead of
	// making the leaf spend a turn asking for a tool it demonstrably needs.
	DocumentPaths []string
	// Reflex constrains the general loop to one obvious micro-action and gives
	// it an explicit promotion verdict when the assignment is larger than it
	// first appeared.
	Reflex bool

	// Fold says this leaf's material is entirely in Inputs above, whole, and
	// that its job is to assemble it — so the loop runs it as a fold: one model
	// call, and a second only if the first asked for a tool or produced nothing
	// deliverable. See foldTurns.
	//
	// It is set by whatever assembled this task, from two structural facts it
	// can check and this package cannot: that the plan gives this node nothing
	// to go and find (plan.Folds), and that every dependency arrived pushed
	// rather than clipped to a handle. The second is what makes the first safe —
	// a node told to assemble material it was only handed a pointer to has to go
	// and get it, whatever its plan says.
	Fold bool

	// Subharness names the worker this leaf was routed to. It is carried on the
	// task rather than looked up again at dispatch because the choice was made
	// once, upstream, and journaled: the scheduler's job is to honour it, not to
	// re-decide it. Empty is the generalist, which is nearly every leaf.
	Subharness string

	// Skills is the ordered list of skill names attached to this leaf's brief:
	// the plan composed them from the shelf (pinned first), and the brief
	// renders them beside the working method. Empty renders nothing.
	Skills []string

	// Steer, when set, is polled between turns for mid-flight guidance from
	// the user. Each returned line lands in the transcript as a user message
	// before the next model call, so a running worker can be redirected
	// without being killed. Nil (the default, and the whole one-shot path)
	// costs nothing.
	Steer func() []string
	// Share, when set, gives the worker a one-line channel to the rest of its
	// job: a discovery about the material, a pitfall, a decision siblings must
	// match. It is nil for a job with no siblings, so a single-worker errand
	// never pays the schema for a channel with nobody on the other end.
	Share func(line string) error
	// Board is the reading side of Share: polled at the same between-turn
	// boundary as Steer, it returns lines other workers on this job shared.
	// They land in the transcript in their own voice, never the user's — a
	// sibling's discovery is testimony, not instruction.
	Board func() []string
	// Control is polled at the same between-turn boundary as Steer. It is
	// deliberately cooperative: a model/tool turn already in flight lands,
	// then the claim owner releases through the store CAS path.
	Control func() ControlAction

	// Progress is within-node visibility: where the work has got to, said in a
	// way that replaces the last thing it said rather than adding to it.
	//
	// It exists for the leaf that is long and whose insides are not nodes. A
	// linear leaf is a turn loop nobody watches and passes nil; a saved program
	// that runs a pipeline for forty minutes would otherwise be a spinner, and
	// the two alternatives to this are both worse — splicing its stages into
	// the graph would put work in the plan that nobody planned, and posting
	// them as thread messages would spend the person's attention on a running
	// pipeline. phase is the coarse thing being done, done/total are a count
	// when there is one, and latest is the short right-hand side. Nil-safe and
	// ignored when nil, so no existing caller pays anything for it.
	Progress func(phase string, done, total int, latest string)

	// Fault carries a recovered panic out to whoever can write it down.
	//
	// It exists because guard.Note's whole record is a line in a log file, and a
	// caught fault is a fact about the run that changes what the person watching
	// should expect. In the crashed run of 2026-08-28 a leaf faulted, was
	// escalated two seconds later, and the headless stream said `still waiting`
	// for eleven minutes; the operator read it as a hang. The surface that owns
	// the journal is the one that can journal it, so the leaf-running code
	// reports and the surface records. Nil-safe and ignored when nil, so a
	// caller with nowhere to write pays nothing.
	Fault func(error)
	// contains filtered or unexported fields
}

Task is one leaf, ready to run.

func (Task) Faulted

func (t Task) Faulted(err error)

Faulted reports one recovered panic, and reports nothing at all when the surface offered nowhere to write it. The nil check lives here for the same reason [Task.progress]'s does, and it matters more: every call site is inside a deferred recover, which is exactly where a second panic is unrecoverable.

type TaskInput

type TaskInput struct {
	// Brief is the self-contained instruction and the one required field. A leaf
	// with no brief has been handed nothing to do.
	Brief string `json:"brief"`
	// Goal is the whole plan's aim, for orientation. Empty where there is no
	// wider plan, which is every direct invocation.
	Goal string `json:"goal,omitempty"`
	// Contract is the working method — how this kind of job is done well.
	Contract string `json:"contract,omitempty"`
	// Title is what the work is called on a roster row.
	Title string `json:"title,omitempty"`
}

TaskInput is the typed front door of every subharness that is really a leaf worker. It is Task's four prose fields and no more, because those are the four things a leaf is actually told: what the whole job is for, what THIS piece is, how this kind of work is done well, and what to call it.

type TaskOutput

type TaskOutput struct {
	Result    string   `json:"result"`
	Artifacts []string `json:"artifacts,omitempty"`
}

TaskOutput is what such a worker promises: its final message, and the files it left behind.

type ToolResult

type ToolResult struct {
	Text string
	JSON json.RawMessage
	// Spend is what the call cost where a tool spends model tokens of its own.
	// Most tools spend nothing and leave it zero.
	Spend Spend
}

ToolResult is what one belt tool produced. It is text plus, where the tool speaks JSON, the same answer structured — the belt's tools mostly answer in prose, and a result type that demanded JSON of all of them would make every caller invent a wrapper.

type Toolbox

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

Toolbox executes tool calls against one workspace on behalf of one node.

func NewToolbox

func NewToolbox(workspace *Workspace, leaf string, web *Web) *Toolbox

NewToolbox builds a toolbox for a caller that cannot say what its model holds. That is not a guess about a small model — it is the honest unknown, and it resolves to the bounds this package has always used.

func NewToolboxWithStore

func NewToolboxWithStore(workspace *Workspace, leaf string, web *Web, history *store.Store) *Toolbox

NewToolboxWithStore adds persistent recall to the generic toolbox. A nil store deliberately collapses to NewToolbox so one-shot leaves retain the base-definition prompt.

func (*Toolbox) Arm

func (t *Toolbox) Arm(names ...string)

Arm admits named capability families or individual tools for the rest of this leaf's life. It is how a structural fact about the assignment — an attached image, an attached document — buys back exactly the schema it justifies before the first turn, and how the discovery tool answers a worker that asked.

func (*Toolbox) Close

func (t *Toolbox) Close() int

Close is used by leaf teardown and tests. Log files and final registry state remain; only surviving process groups are terminated.

func (*Toolbox) Definitions

func (t *Toolbox) Definitions() []ai.ToolDefinition

Definitions are what the model sees. Descriptions are terse because they are resent every turn, but each one states the thing an agent gets wrong without being told.

The ORDER is load-bearing and is the second half of the cache-shape fix. Tool definitions ride at the very front of every request, ahead of the whole transcript, so the first byte of this block that differs between two calls re-bills everything behind it at full price. The list is therefore built in three strata, widest agreement first:

  1. the five tools every leaf on every machine always has, in a fixed order;
  2. the discovery tool, present for the life of any leaf whose machine has an optional family configured — a property of the brain, never of what this leaf has armed, so it never appears or vanishes mid-run;
  3. per-leaf conditionals (recall, share) and then the armed schemas.

Only stratum 3 can move, and it can only ever grow at the tail: arming a family appends, so the invalidation is bounded by the schemas actually added rather than by everything that used to sit behind the thing that moved.

func (*Toolbox) Execute

func (t *Toolbox) Execute(ctx context.Context, name string, arguments string) (outcome Result)

Execute dispatches one call. An unknown name is answered with the valid list rather than refused, because a model that guessed a tool name can recover from being told the real ones and cannot recover from a dead loop.

func (*Toolbox) ForceClose

func (t *Toolbox) ForceClose() int

ForceClose is the scheduler-abandonment path. A wedged leaf cannot leave a keep request behind without a resident available to decide it.

func (*Toolbox) Guidelines

func (t *Toolbox) Guidelines() []string

Guidelines returns the guidance lines for the tools this leaf is actually holding, in the order the tools are defined. It is read once, when the system message is assembled: the set of tools a leaf holds is fixed at that point except for arming, and arming appends schemas rather than rewriting the standing text — a system message that changed mid-run would cost the whole prompt at full price on the turn it changed.

func (*Toolbox) ServiceRequests

func (t *Toolbox) ServiceRequests(leafNodeID string) []ServiceRequest

ServiceRequests snapshots live promotion leases before leaf teardown.

type TranscriptJournal

type TranscriptJournal interface {
	RecordTranscript(nodeID, model string, entries []store.TranscriptEntry) error
}

TranscriptJournal is the narrow slice of *store.Store the recorder writes through. It is an interface so a test can hold the batches without a database, and so this package's dependency on the store stays one method wide.

type TranscriptRecorder

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

TranscriptRecorder is the store-backed sink: it batches a leaf's entries and writes each batch to the journal under the node.

IT BATCHES, AND IT DOES NOT WAIT UNTIL THE END. One event at settlement time would be cheaper and would lose the whole record of every leaf that died before settling — which is the failure this machinery exists for. So a full batch is written the moment it fills, and the runner flushes the remainder on every exit. A hard kill of the process costs at most the last partial batch.

THE WRITE HAPPENS UNDER THE LOCK, synchronously, in the caller's goroutine. An async writer would buy back a few milliseconds per sixty-four entries and would owe a shutdown handshake that a panicking leaf cannot perform; the turns it records are separated by provider round-trips measured in seconds, so there is no hot path here to protect.

func (*TranscriptRecorder) Flush

func (r *TranscriptRecorder) Flush()

Flush makes everything recorded so far durable. It is a no-op when there is nothing pending, which is what makes it safe to call from several exits.

func (*TranscriptRecorder) Record

func (r *TranscriptRecorder) Record(entry store.TranscriptEntry)

Record takes one entry, bounding it and writing the batch out when it fills.

type TranscriptSink

type TranscriptSink interface {
	Record(entry store.TranscriptEntry)
	Flush()
}

TranscriptSink receives one leaf's turns as they happen.

Record is called from the loop goroutine, and may be called concurrently by tools that run in parallel, so an implementation must be safe for concurrent use. Flush makes everything recorded so far durable and must be safe to call more than once, including after the run is over: the runner flushes on every way out of a leaf, and a leaf that is abandoned on the clock goes on recording into a sink that has already been flushed once.

func NewTranscriptRecorder

func NewTranscriptRecorder(journal TranscriptJournal, nodeID, model string) TranscriptSink

NewTranscriptRecorder builds the sink for one attempt at one node. The model is the one the leaf was dispatched on, journaled beside the entries so a reader of a re-run node can tell which attempt was which.

It returns the INTERFACE rather than the concrete type, and returns a literal nil when there is nothing to write to. A *TranscriptRecorder(nil) handed to WithTranscript would be a non-nil interface holding a nil pointer — safe to call, because every method here guards its receiver, but indistinguishable from a live sink to the loop, which would then pay to build and truncate every entry so the sink could throw it away.

func TranscriptFrom

func TranscriptFrom(ctx context.Context) TranscriptSink

TranscriptFrom is the sink this context carries, or nil when nobody is listening. Nil is returned rather than a no-op sink on purpose: a caller that holds nil can skip building the entry at all, and building an entry means copying and truncating a tool result that may be fifty kilobytes.

type TreeSnapshot

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

TreeSnapshot is the workspace as the filesystem itself showed it at one instant: every path a deliverable could be, with enough of each file recorded to tell it apart from its successor without asking the clock what time it is.

It is ONE type with ONE walk behind it, and that is the point of it. The whole-leaf evidence record (WatchTree at the top of a run, RecordChanges at landing) and the per-call sweep after every tool call are the same question asked over different spans — "what did the tree gain, lose or change between these two moments" — and two implementations of one question are two answers waiting to disagree about what a deliverable is.

type TurnUsage

type TurnUsage struct {
	// Turn is the leaf's own turn number, counting from one.
	Turn int `json:"turn"`
	// Usage is this turn's whole bill, model call and tools together.
	Usage
	// Sent is in_k: what this turn's own model call put on the wire, the whole
	// prompt including the part the provider served from cache. Summed across
	// turns it is the leaf's cumulative context pressure — the quantity a
	// per-turn window bound cannot see because no single turn is large, and a
	// cache-discounted spend meter cannot see because most of it was cheap.
	Sent int `json:"sent"`
}

TurnUsage is one turn of one leaf: the model call it made, plus whatever its tools spent on its behalf while it ran.

Tool spend is folded into the turn that caused it rather than kept aside, because the invariant that makes this ledger trustworthy is that the rows sum to the node row — a reader who has to remember which costs were left out has been handed a second, quieter accounting rather than a finer one.

type UnwiredEnv

type UnwiredEnv struct{}

UnwiredEnv is an Env with nothing behind it. Every door answers NotWired for its own name.

IT IS SCAFFOLDING AND IT SAYS SO. The lanes building the runtime, the store and the surfaces all compile against Env before any of them has a working host, and a shared honest stub is what lets them do that without three private fakes that drift. It is NOT the "absent, not broken" pattern reaching a person: a door that hands a subharness one of these has no subharnesses to offer, so nothing about them is drawn at all.

func (UnwiredEnv) AI

func (UnwiredEnv) Ask

func (UnwiredEnv) Log

func (UnwiredEnv) Recall

func (UnwiredEnv) Recall(context.Context, string) ([]Note, error)

func (UnwiredEnv) Remember

func (UnwiredEnv) Remember(context.Context, string) error

func (UnwiredEnv) Tool

type Usage

type Usage struct {
	Calls            int     `json:"calls"`
	PromptTokens     int     `json:"prompt_tokens"`
	CompletionTokens int     `json:"completion_tokens"`
	CachedTokens     int     `json:"cached_tokens"`
	Cost             float64 `json:"cost"`
}

Usage is the running cost of one task.

type Web

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

Web is search and page fetching, the one capability a shell does not already have. Fetching is plural because the shape of research is search once, then read several things — doing that one page at a time turns a single round-trip into five, each carrying the whole accumulated context.

func NewWeb

func NewWeb() *Web

NewWeb is always available: fetching needs no key at all, and search degrades from Exa (when EXA_API_KEY is set) to DuckDuckGo's keyless HTML endpoint rather than disappearing. A worker without internet is a worker that fails jobs it could have finished.

func (*Web) Fetch

func (w *Web) Fetch(ctx context.Context, urls []string) string

Fetch retrieves several pages at once and returns them as text. A page that fails is reported in place rather than failing the batch — one dead link should not cost the other four.

func (*Web) Search

func (w *Web) Search(ctx context.Context, query string, limit int) (string, error)

Search returns ranked results with enough text to judge them. Exa answers when a key is configured; DuckDuckGo covers both the keyless case and an Exa outage, so one provider having a bad day never blinds the workforce.

type Workspace

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

Workspace is the shared directory a run writes into.

It is shared rather than per-node on purpose. A dependent that needs the full text of an upstream artifact reads the file instead of receiving it inline, which is what keeps a large deliverable out of three contexts at once. That only works if there is one directory everyone can see.

Collisions are avoided by construction rather than by locking: each node is given a distinct suggested output path derived from its id and title, and nodes that run at the same time are independent by the graph's own definition.

func NewWorkspace

func NewWorkspace(root string) (*Workspace, error)

func (*Workspace) ArtifactFacts

func (w *Workspace) ArtifactFacts(leaf string) []ArtifactFact

ArtifactFacts is the whole evidence record for one leaf, in stable path order: every file a tool claimed, every file the tree was seen to gain or change, and every file the tree was seen to LOSE.

Deletions live here and deliberately not in Workspace.Artifacts. That list is a list of things to open — it becomes the head's files line, a dependent's "(files: …)" pointer, and the gate's roll call of what is on disk — and a path that no longer exists sends every one of those readers to nothing. The fact that the leaf removed it is still evidence about the run, so it is kept, in the one place whose readers are asking what happened rather than what to read.

func (*Workspace) Artifacts

func (w *Workspace) Artifacts(leaf string) []string

Artifacts lists what a node left behind for the person who asked, in stable order. It is the union of two independent accounts, and it is a union because each of them alone has been measurably wrong.

The CLAIMED account is the write family's: a path is here because a tool recorded it, which is also the worker's own statement that this file is what its work was for. The OBSERVED account is the filesystem's: a path is here because the tree gained or changed it while this leaf was running, whatever wrote it. Neither subsumes the other. A leaf that writes with a shell command is invisible to the first — on 2026-08-28 the delivery gate's audit said "the record shows nothing named out.txt was left behind" while out.txt sat on disk, and convicted a correct deliverable on that. A file rewritten inside one coarse filesystem second at exactly its old length is invisible to the second, and the tool that wrote it is not.

The harness's own records are held back whichever account saw them: they flow into Outcome.Artifacts, from there into the head's files line and into every downstream leaf's "(files: …)" pointer, and none of those is a place to name a log. So is a deletion — see Workspace.ArtifactFacts for where deletions are kept and why they are not here.

func (*Workspace) DirectoryAt

func (w *Workspace) DirectoryAt(path string) bool

DirectoryAt reports whether a directory already occupies a path.

It exists for the one caller that offers a path rather than reading one: an output hint is an invitation to write a FILE at a name, and a directory already sitting at that name makes the invitation unfulfillable. A leaf handed it anyway did the only thing left — wrote its deliverable inside — reported the file as written, and settled done with the asked-for file absent from the workspace. Nothing here creates the path: an offered address that the offer itself brings into existence is a directory the next leaf must write around.

func (*Workspace) Existing

func (w *Workspace) Existing() []string

Existing lists the files already sitting in the workspace, as absolute paths in stable order.

It answers the one question the in-memory artifact register cannot: what did a PREVIOUS process leave here. A leaf whose run was interrupted — by its own time ceiling, or by the terminal closing — comes back to a fresh Workspace whose register is empty and a directory that is not, and the files in it are the whole of what that attempt has to hand on. Reading them off disk is the only honest source, because the register never survived.

Only the top level, and never the harness's own dot-directories: a deliverable is written where the output hint points, which is here, and everything below a dot is machinery an agent was deliberately not shown.

func (*Workspace) HoldsNothingBut

func (w *Workspace) HoldsNothingBut(named []string) bool

HoldsNothingBut reports that the working directory contains no file a leaf could go and discover other than the ones named.

It is the measured half of the sufficiency claim the brief makes (see [Linear.brief]). A leaf may only be told that its brief is the whole of what exists for its job if that is a fact about this directory, and the only honest way to hold a fact about a directory is to read it. So it is read: one bounded walk, at task assembly, skipping dot-entries — machinery lives outside the root now and .git is the tooling's — and the dependency trees producedSkipDir already names as somebody else's files.

Cheap by construction and by shape. A harness-made job directory answers in one syscall because it is empty or holds only its siblings' deliverables; a person's repository answers false on the first source file it meets, before it has walked anything. An unreadable or unreasonably large tree answers false, because "could not tell" and "there is material here" must lead to the same silence.

func (*Workspace) Locate

func (w *Workspace) Locate(path string) (string, bool)

Locate maps a recorded artifact path back onto disk.

Recorded paths are workspace-relative, and a caller that stats one directly measures whatever sits at that name under its own working directory — usually nothing. An absolute path is no safer: the root has two honest spellings whenever it sits under a symlink (macOS /tmp -> /private/tmp), and only one of them is the spelling the file was recorded with. Both are tried here so the caller never has to know which one it holds.

func (*Workspace) MutationCount

func (w *Workspace) MutationCount(leaf string) int

MutationCount is the revision of one leaf's successful filesystem actions. Unlike len(Artifacts), it advances when an existing deliverable is edited a second time and when a shell call changes or deletes a path already known.

func (*Workspace) OwnedByPerson

func (w *Workspace) OwnedByPerson() *Workspace

OwnedByPerson records that this root is somebody's own directory. It is the one caller whose workspace is not its own — `codeaf do -w` edits a person's project in place — and it is said explicitly rather than inferred from where the machinery went, because the machinery now always goes elsewhere.

func (*Workspace) PersonalRoot

func (w *Workspace) PersonalRoot() bool

PersonalRoot reports that the root belongs to a person rather than to the harness.

It is asked by any worker that would otherwise take the directory over: a directory the harness made for a job may be checked out, reset and swept; a directory somebody handed us holds their work and none of that is ours to do.

func (*Workspace) Record

func (w *Workspace) Record(leaf string, path string)

Record notes that a node produced a file the person who asked for the work would call a deliverable.

leaf is the identity everything one worker writes is filed under, and it is a string rather than a number for the reason SuggestPathFor is: the identity a caller has is not always a per-node integer. A store node's creation sequence is its whole splice's, so five siblings recorded under it shared one bucket and each of them was told the other four's files were its own. See Task.NodeKey for who supplies what.

func (*Workspace) RecordChanges

func (w *Workspace) RecordChanges(leaf string)

RecordChanges reads the tree again and files everything that moved since Workspace.WatchTree under this leaf's identity. It is the observed half of Workspace.Artifacts and the only half that is true of a file written by a shell command, a build, or anything else that never went through a tool.

It walks rather than being folded into Artifacts because Artifacts is read on the leaf's hot path — the no-progress guard counts it twice a turn — and a tree walk per turn is a cost that buys nothing there: the toolbox already records what each shell call produced as it goes. This is the whole-leaf backstop for everything that record misses, and it runs once, at landing.

Calling it more than once is safe and cheap-ish: each call re-reads the tree and merges, so a leaf that lands and then terminates background jobs can ask again and pick up what those jobs left.

func (*Workspace) RecordInternal

func (w *Workspace) RecordInternal(leaf string, path string)

RecordInternal notes a file the harness wrote for its own purposes — a background job's log, an extracted-document cache. They are real files in the workspace and the bookkeeping should know about them, but they are not the job's output: named to the user as "the files that job wrote", a process log and a PDF text dump stand beside the actual report as if they were peers. The .obs spill directory already solves this by never calling Record at all; these two cases need the record and only want it out of the answer.

func (*Workspace) RecordProducedSince

func (w *Workspace) RecordProducedSince(leaf string, before *TreeSnapshot)

RecordProducedSince files what the workspace gained, changed or lost between before — a Workspace.Snapshot taken at the top of one tool call — and now, under leaf, in the same registry a write goes into.

It is the ONE door for an executor whose tools do not report their own writes — a shell command, or a tool ported from elsewhere that knows a directory and nothing of this workspace — because what the delivery gate is later shown is this registry and nothing else: a leaf that wrote the file and never filed it is convicted of not writing it, and a second leaf is spliced in to write it again. A belt whose tools file nothing paid that twice on every run until it went through this door.

The findings go to BOTH accounts, and deliberately. A created or changed file is recorded as a deliverable, which is what the footer and the gate read and is the behaviour every caller already depends on; and every finding, deletions included, is recorded as OBSERVED, because a before-and-after read of the tree is the world's own testimony whether it spans a whole leaf or one call. A deletion cannot be a deliverable — there is nothing to open — so it is kept only in the evidence record, exactly where Workspace.RecordChanges keeps one.

func (*Workspace) Resolve

func (w *Workspace) Resolve(path string) (string, error)

Resolve maps a workspace-relative path onto disk, refusing anything that climbs out. Safety is not the point here — the point is that a path escaping the workspace is almost always a confused agent rather than an intended one, and failing loudly gives it something to correct.

func (*Workspace) Root

func (w *Workspace) Root() string

Root is the absolute directory.

func (*Workspace) ScratchPath

func (w *Workspace) ScratchPath(relative string) (full, shown string, err error)

ScratchPath maps a harness-owned relative path onto disk and returns, beside it, the spelling to show a model. The two differ only when scratch has been moved out of the workspace: a relative path would then name nothing an agent could open from its own cwd, so it is shown the absolute one.

func (*Workspace) ScratchRoot

func (w *Workspace) ScratchRoot() string

ScratchRoot is where this workspace's machinery lands. It equals Root only for a caller that never named one, which in the product is nobody.

func (*Workspace) Size

func (w *Workspace) Size(path string) (int64, bool)

Size reports an artifact's size on disk. The second result separates a file that is empty from one that is not there — a summary listing every artifact as 0 bytes looks like a run that produced nothing.

func (*Workspace) Snapshot

func (w *Workspace) Snapshot() *TreeSnapshot

Snapshot photographs every file a deliverable could be. It is what a caller holds across a span it wants the truth about — a whole leaf, or one tool call — and hands back to Workspace.RecordChanges or Workspace.RecordProducedSince at the other end.

What it skips is exactly what the per-command sweep skips, from the same predicate: dot-entries, which are the harness's own machinery (.obs spills, .codeaf job logs and traces) and the tooling's (.git, editor state), and the dependency trees producedSkipDir names — node_modules, vendor, site-packages, __pycache__, bower_components, venv. One predicate rather than two, because two lists of "what is not a deliverable" is two answers to one question.

THE WALK IS BOUNDED AT producedScanLimit ENTRIES, which is 6000, AND THE BYTES IT READS AT snapshotDigestBudget. A workspace is usually a handful of files; a person's repository is not, and this runs at both ends of every leaf and both ends of every tool call. Past either bound the answer is partial or a stamp is digestless, and both of those are read as less than a whole answer rather than as a different one. PERF.md carries the budget.

func (*Workspace) WatchTree

func (w *Workspace) WatchTree(leaf string)

WatchTree remembers the workspace as it is right now, so that what a leaf changes can be told from what was already sitting there.

Every executor calls it once at the top of a run and calls Workspace.RecordChanges at landing; between the two, the difference is this leaf's mark on the world. A second run of the same leaf re-baselines, which is correct — the observations already made are kept, and a retry is only asked what IT did — and a leaf that was never watched simply has no observed account, which is the pre-existing behaviour and never a wrong answer, only a narrower one.

func (*Workspace) WithScratch

func (w *Workspace) WithScratch(dir string) *Workspace

WithScratch sends the harness's own files somewhere other than the workspace. Every surface that runs leaves calls it — see [Workspace.scratch] for what it costs when nobody does. A caller that does not (a test, an embedder) keeps the old shape, which is the workspace itself.

Directories

Path Synopsis
Package bare — edit matching logic, pinned to pi's edit-diff.js.
Package bare — edit matching logic, pinned to pi's edit-diff.js.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL