subharness

package
v0.5.2-rc.1 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: 20 Imported by: 0

Documentation

Overview

Package subharness is the registry of node kinds a sub-harness is built from, and the durable home of the sub-harnesses themselves.

A sub-harness is a registry entry, not generated code: identity, a program that is a DAG over the node kinds below, a tool whitelist, a verification rung, a dynamism rung with its budget, and tests. The kinds are a library-in-binary — `agent.loop` is the session loop this binary already runs, `tool.call` is the tool call it already makes — so a saved harness is a small, inspectable arrangement of machinery that exists, and never a program the resident has to compile.

Two ladders run through everything here, both adopted from the Agent-Field skill and both carried as an argument rather than a design:

  • Verification: accept < schema < invariants < loop < report < rederive < adversarial < human. How hard a run's output has to work to be believed.
  • Dynamism: fixed < branch < width < meta < recursive < selfmod. How much shape a run is allowed to decide for itself, with an integer Cap that is the budget for deciding it.

The dynamism rung is enforced against the program: a `fixed` harness may not contain a `branch` node, a harness that never declared `recursive` may not call another sub-harness. That is the whole point of writing the rung down — a harness cannot quietly become more autonomous than its author said.

Version is a pointer. v1, v2, v3 are immutable pages under the harness's own directory, and opening a pointer again reads the same page it read before; see store.go. Design: docs/SUBHARNESS.md.

Package subharness is the sub-harness registry as far as anything outside the engine has to know it: what one entry IS, and how a person's turn is matched against the entries this build holds (docs/SUBHARNESS.md).

The engine — the node kinds, the program over them, the verify ladder, the run traces — lands beside this. What is here is the half a CONVERSATION needs, and it is deliberately the smaller half: a name, a sentence, the words a designer said this harness answers to, and a pure function from a turn to a number. Nothing in this package runs anything, reads a file, or calls a model.

Index

Constants

View Source
const (
	RelDepends     = "depends"
	RelIndependent = "independent"
)

The two words a pair may be marked with, and they are about the TOPOLOGY rather than about which node reads which: `depends` is "one of the two is downstream of the other"; `independent` is "neither is downstream of the other, so they could sit in separate lanes".

The distinction is worth the sentence because it is where designs are lost. A check at the end of a chain reads only the summary in front of it and is still downstream of the gathering three steps back — the check below asks reachability, so that pair is `depends`, and a designer reasoning from "does it read the output" writes `independent` and is refused. The guide (prompts/designer.md, PART THREE) is written to say it the way this reads it.

View Source
const (
	Threshold   = 0.55
	ClearWinner = 0.25
	Floor       = 0.40
)

The decision Best makes, in three numbers.

  • Threshold is the score at which one match stands on its own. It is met, not exceeded: 0.55 is the number one whole phrase clears and one lone word does not.
  • ClearWinner is the other way in. A registry is a field of candidates, and an entry that is a quarter of the scale ahead of everything else is the answer to the question even when it is a middling score in absolute terms. With one entry in the registry the runner-up is 0 and this rule is the whole of the decision — which is right: a build with one harness has nothing else the sentence could have meant.
  • Floor is the line under which neither rule may reach. Below 0.40 the evidence is a word or two of prose overlap, and a card raised on that is the card that teaches people to say no without reading.
View Source
const (
	KindAgentLoop      = "agent.loop"
	KindToolCall       = "tool.call"
	KindParallelSplit  = "parallel.split"
	KindParallelJoin   = "parallel.join"
	KindBranch         = "branch"
	KindLoopUntil      = "loop.until"
	KindHumanGate      = "human.gate"
	KindVerify         = "verify"
	KindSubharnessCall = "subharness.call"
	KindTrigger        = "trigger"
)

The library-in-binary. Every kind here names machinery this binary already has: agent.loop is the session loop, tool.call is the tool call, human.gate is the ask the head already knows how to raise. A sub-harness arranges them; it never adds a tenth thing.

Each kind carries the lowest dynamism rung a harness may declare and still contain it. That mapping is the enforcement point for the whole autonomy spectrum, so it is written once, here, beside the kind it bounds:

fixed      agent.loop, tool.call, verify, human.gate, trigger
branch     branch, loop.until          — the run picks a path
width      parallel.split, parallel.join — the run picks how many
recursive  subharness.call             — the run enters another harness

meta and selfmod sit above these and unlock nothing new by themselves; they are rungs a harness declares when its agent.loop nodes rewrite their own briefs (meta) or when it may save a new version of itself (selfmod), and both are refused by anything that reads a lower rung.

View Source
const (
	JoinAll   = "all"
	JoinAny   = "any"
	JoinFirst = "first"
)

The join modes. `all` is a barrier — every incoming edge must have run — and it is the default because a join that fires on a partial set is a deliberate choice, not a convenience.

View Source
const (
	TriggerHosted  = "hosted"
	TriggerIdle    = "idle"
	TriggerWatch   = "watch"
	TriggerCommand = "source.command"
)

The trigger sources. `source.command` is the one that carries a payload: the shell command whose output is the trigger's reading.

View Source
const (
	// OpReplaceBrief rewrites one node's brief. It is set_field's most common
	// case, named on its own because rewriting a brief is most of what a review
	// does and a critic reaching for the general form to do the common thing is
	// a critic one field name away from a skipped op.
	OpReplaceBrief = "replace_brief"
	// OpSetField writes any field of any node. An empty `text` REMOVES the
	// field, which is how a `max_turns` nobody counted goes away.
	OpSetField = "set_field"
	// OpAddNode adds a node, whole, from `node_json`.
	OpAddNode = "add_node"
	// OpDropNode removes a node AND every edge that touched it. The critic has
	// to re-link what it disconnected — the reach law is checked at the end.
	OpDropNode = "drop_node"
	// OpAddEdge and OpDropEdge carry the edge as `node` → `text`.
	OpAddEdge  = "add_edge"
	OpDropEdge = "drop_edge"
	// OpSetVerify moves the harness's verification rung, or — when `node` names
	// one — that node's own rung.
	OpSetVerify = "set_verify"
	// OpSetDyn moves the dynamism rung or its cap: `field` is "ladder" or "cap".
	OpSetDyn = "set_dyn"
	// OpSetWhitelist replaces the tool whitelist with a comma-separated list. It
	// is here because dropping the last node that used a tool leaves a grant
	// nothing uses, and a critic that could not answer for that would be refused
	// for the tidying it was asked to do.
	OpSetWhitelist = "set_whitelist"
	// OpSetDesc rewrites id.desc — the sentence detection matches on, which the
	// review's own checklist asks the critic to read against the goal.
	OpSetDesc = "set_desc"
)

The ops. Each one is the smallest edit that is worth naming, and there is deliberately no op for "replace the program": a critic that wants a different program is designing, not reviewing.

View Source
const (
	// MaxWidth bounds one parallel.split. Wider than this is a job for the
	// planner, not for a node.
	MaxWidth = 16
	// MaxRounds bounds one loop.until. Past a handful of rounds the condition
	// is wrong or the work is, and either way another round will not find out.
	MaxRounds = 8
	// DefaultRounds is what a loop.until that declares nothing gets.
	DefaultRounds = 2
	// MaxTurns bounds one agent.loop's conversation with itself.
	MaxTurns = 64
	// MaxDynCap bounds the dynamism budget — the total number of runtime
	// decisions (extra loop rounds, minted width) a run may spend.
	MaxDynCap = 32
	// MaxNodes refuses a pathological program at parse time. A shape bigger
	// than this is a plan, and plans belong to the planner.
	MaxNodes = 64
	// MaxIdBytes is how long a node id may be. Node ids are read by people in
	// a trail and typed back by hand; past this they stop being either.
	MaxIdBytes = 48
	// MaxVersion bounds a version pointer. It is a cap on a field a file may
	// declare — a subharness.call pinned to v10000 is a typo, not a pin.
	MaxVersion = 9999
)

The integer caps. Every number a harness file may declare is bounded here rather than at the point of use, because the file is written by a model and the cost of a mistyped width is paid in fan-out, not in an error message.

View Source
const (
	VerifyAccept      = "accept"
	VerifySchema      = "schema"
	VerifyInvariants  = "invariants"
	VerifyLoop        = "loop"
	VerifyReport      = "report"
	VerifyRederive    = "rederive"
	VerifyAdversarial = "adversarial"
	VerifyHuman       = "human"
)

The verification ladder, weakest first. A rung is an argument to the harness and, per node, to a verify node — never a separate design.

View Source
const (
	DynFixed     = "fixed"
	DynBranch    = "branch"
	DynWidth     = "width"
	DynMeta      = "meta"
	DynRecursive = "recursive"
	DynSelfmod   = "selfmod"
)

The dynamism ladder, least autonomous first.

View Source
const (
	SalvageStrict   = "strict"
	SalvageExtract  = "extract"
	SalvageSanitize = "sanitize"
	SalvageLenient  = "lenient"
)

The rung names, in the order they are climbed.

View Source
const (
	// Root is the directory name under the state root.
	Root = "harnesses"
	// RunDir is where a run's trace lands, under the harness's directory.
	RunDir = "run"
)

The layout under the state root. One directory per harness, immutable version pages inside it, and the runs beside them:

~/.codeaf/harnesses/<name>/v1.json
~/.codeaf/harnesses/<name>/v2.json
~/.codeaf/harnesses/<name>/run/20260816T101112Z.json

There is no head file. The head is the highest page present, which means the pointer cannot disagree with the pages it points at — the failure mode a separate head file exists to have.

View Source
const BriefField = "brief"

BriefField is the one thing a page is asked for before it runs.

A PAGE TAKES ONE FREE-TEXT REQUEST AND THE OLD FORM CANNOT TAKE MORE: the runner threads a single string through the program (exec_model.go), so a manifest declaring a second field would be a card asking for something nothing downstream could read. One required field, named for what it is, is the whole of the honest front door.

View Source
const MaxCallDepth = 3

MaxCallDepth bounds subharness.call nesting. It is a runtime bound rather than one of the file's caps (registry.go): a page cannot declare it, because the depth is a property of who called whom, and the recursion it stops is a fan of three harnesses that each thought they were the top one.

Variables

View Source
var ErrNotFound = errors.New("subharness: not found")

ErrNotFound is what a read returns for a harness or a version that was never saved. Callers distinguish "no such harness" from "the disk is broken", so this is a sentinel rather than a formatted string.

Functions

func ApplyReport

func ApplyReport(h Harness, ops []Op) (Harness, []OpResult, error)

ApplyReport is Apply with the fate of every op, for a caller that prints the patch as the delta.

func Card

func Card(h Harness) string

Card is the harness as a person reads it, as one block of text.

func CardLines

func CardLines(h Harness) []string

CardLines is the card, one string per line, unstyled. Nothing here is padded to a width: the surfaces that care about width own their own fitting, and a renderer that guessed at one would be guessing for the narrowest reader.

func Catalog

func Catalog() string

Catalog is the kind catalog the designer's guide teaches from, rendered from the registry itself: the fields the model is told about are the fields Validate will enforce, in the order kinds.go declares them. A kind added to the registry appears here, and a field renamed changes the guide and the law in one edit — the guide cannot describe a field the page refuses, nor refuse one the guide never mentioned.

func CheckDerivation

func CheckDerivation(h Harness, pairs []Derivation) error

CheckDerivation holds a derivation table to the program it was drawn for.

An ABSENT table is not an error. The check is a new law and a page designed by an older prompt has no table to hold; refusing those would fail designs for having been made yesterday. A table that IS present is held to every line of itself, because a table that can be switched off by a typo in `rel` is not a machine check.

A pair NOT in the table is not an error either. The edges are the truth about dependency; the table is a claim about the edges, and silence makes no claim.

func CommandFor

func CommandFor(name string) string

CommandFor is the hosted command line for a harness name. It is derived and never configurable: `/harness <name>` means one harness by construction, so two entries cannot collide on a line and no file can squat a word that belongs to the product.

func DynLadder

func DynLadder() []string

DynLadder returns the rungs in order, least autonomous first.

func DynRung

func DynRung(word string) int

DynRung is a rung's height, or -1 for a word that is not on the ladder.

func Encode

func Encode(h Harness) ([]byte, error)

Encode writes a page: indented, newline-terminated, and with HTML escaping off so a brief that contains an ampersand reads back the way it was written.

func LastRunCost

func LastRunCost(usd float64) string

LastRunCost is what a run cost, in the cells a dim row can spare. Cents while a run is cheap, so a program that spent a fraction of one is not drawn as though it had spent nothing — which is the same ladder the status line's own reading takes, minus its zero rung: a zero never reaches here, because a cost nobody reported is not drawn at all.

func LastRunLine

func LastRunLine(run LastRun, now time.Time) string

LastRunLine is the reading as one short line: when, how it went, and what it cost.

The run's own sentence about what ran out is NOT on this line. It was written to be read in full, this is one dim line under a name, and a row that wrapped would cost the list the scannability the line exists for.

func Machinery

func Machinery(belt []BeltEntry) map[string]string

Machinery is every value the designer's guide leaves a hole for: this package's caps, both ladders, the kind catalog and the tool belt. It is the ONE map both doors that render the guide read — cmd/harness-design and internal/session — and it lives HERE, beside the numbers, because when each door kept its own copy a placeholder renamed in the guide was fixed in one and silently broke the other: the standalone tool could not render a brief for a fortnight after «kinds» arrived and «max_turns» left. prompts.Render refuses a hole nobody filled and a value nothing reads, so this map and that guide cannot drift apart without the next render saying so — and now there is one map to drift.

The name column is sized from the belt rather than fixed, because generate_image is thirteen characters and a fixed width turns the list the designer reads into a ragged one the moment a media verb is present.

func MatchCondition

func MatchCondition(condition string, state State) (bool, error)

MatchCondition evaluates one condition against the state. A condition that does not parse is FALSE and an error — never a quiet true — because the arm it guards is the one thing a person approved on the card, and taking it on a typo would be running something nobody read.

It is not called Match because that name is detection's (Match in detect.go: the harness a turn was matched to), and one package cannot mean two things by one word.

func Register

func Register(k Kind)

Register adds a node kind. It panics on a duplicate, a malformed rung, or a kind with neither specs nor a law of its own, because all three are programmer errors in this package's own init, discovered at process start rather than at the first parse.

func RunCard

func RunCard(t Trace) string

RunCard is RunLines as one block of text.

func RunLines

func RunLines(t Trace) []string

RunLines is one trace as a person reads it: the path the run actually took, one row per step, with the rounds and the failure where they landed.

It is the card's twin and it is deliberately the same shape — id on the left, kind beside it, detail on the right — because the two are read against each other. The question a person opens a trace with is "where did this differ from the card", and two renderings with different columns would make them find that out by eye.

func Salvage

func Salvage(raw string) ([]byte, error)

Salvage turns a model's reply into JSON, or reports why it could not.

The error is written to be HANDED BACK TO THE MODEL: it names the rungs that were tried and quotes the strict decoder's own complaint with the text around the offset, because a repair turn that is only told "invalid JSON" is being asked to guess which of two thousand characters was wrong.

func Score

func Score(turn Turn, entry Entry) float64

Score is how strongly one turn asks for one harness, from 0 to 1. It is pure: same turn, same entry, same number, every time, on every machine.

func SeedCues

func SeedCues(goal string) []string

SeedCues derives a harness's trigger vocabulary from the goal that built it: the phrases in that sentence which say what the work IS, ranked, four to eight of them, and every one of them a run of words the goal itself contains.

It exists because of the hole a real run found. A designer writes cues in the designer's vocabulary; the person types the goal in theirs; and the harness built for that exact sentence scored 0.42 against it. Seeding from the goal closes that by construction — the sentence that commissioned a harness always reaches it — and every paraphrase of that sentence inherits the same phrases minus a word or two, which is what [nearly] is for.

WHAT IT LOOKS FOR is contiguous spans of two to four words that begin and end on a word carrying meaning, hold at most one function word in the middle, and stay inside one clause — "adopt a component model", "ad hoc views", "small open weight llms". A span that opens on a verb of work scores higher, because a verb-object pair is what a person retypes when they want the same job done again. Punctuation is a wall: nothing spans the em dash in "views — investigate", because those are two different things being asked for.

WHAT IT REFUSES is padding. A goal with three good phrases returns three; it will not reach the fourth by handing back a common verb that would make every turn containing it a near miss. The count is what the sentence had to give.

func StepLine

func StepLine(step Trail) string

StepLine is one executed step as a person reads it: the mark, the number, the id, the kind, and then what it left behind or what went wrong with it.

It is exported because a surface watching a run LIVE (RunWatched) draws the same step the card draws when the trace is read back afterwards, and two renderings of one step would let the live row and the report disagree about what happened — which is the drift RunLines itself is written against, one column set over.

func ValidCondition

func ValidCondition(condition string) error

ValidCondition parses one condition without evaluating it, in the words a builder can act on. It is called at parse time so that a program with a broken regular expression in it fails on the card rather than in the fourth round of a loop.

func ValidName

func ValidName(name string) bool

ValidName reports whether a name or node id obeys the slug law.

func Validate

func Validate(h Harness) error

Validate is the whole law of a sub-harness, and it is one function because the checks are not independent: a node's kind decides which fields it may carry, the harness's dynamism rung decides which kinds it may carry at all, and the whitelist decides which tools those fields may name. Splitting them would let a caller run two thirds of the law.

func VerifyLadder

func VerifyLadder() []string

VerifyLadder returns the rungs in order, weakest first.

func VerifyRung

func VerifyRung(word string) int

VerifyRung is a rung's height, or -1 for a word that is not on the ladder.

Types

type BeltEntry

type BeltEntry struct {
	Name  string
	About string
}

BeltEntry is one line of the tool list a designer is shown: a name and a sentence. Each door brings its own belt — the standalone rig's few tools, the chat's wire tools plus whichever media verbs this machine has models for — and everything else in the guide's machinery is this package's.

type CardBlock

type CardBlock struct {
	// Head is the identity line: what it is called, which version, what it is for.
	Head string
	// Steps is the run in the order it runs, nested lanes and all.
	Steps []string
	// Foot is the bounds, one quiet sentence at a time. It is empty for a recipe
	// that decides nothing, checks nothing and was tried on nothing.
	Foot []string
}

CardBlock is the card in the three parts a surface may want to paint differently: what this thing is, what it does, and what it is allowed to do.

It exists because the feed's design card is a QUESTION — somebody is about to keep this or drop it — and on that block the steps are the thing being read while the bounds are the quiet aside under them. A surface that wanted those two tiers had only one way to get them before, which was to re-spell the card itself; that second rendering is the drift Card is written against.

THE PARTS ARE THE WORDS AND THE JOINING IS NOT. Where the blank lines go is CardLines's, said once, so a surface that lays the parts out its own way and a surface that prints the block get the same sentences either way.

func CardParts

func CardParts(h Harness) CardBlock

CardParts is that reading of one harness.

type Derivation

type Derivation struct {
	A   string `json:"a"`
	B   string `json:"b"`
	Rel string `json:"rel"`
	Why string `json:"why,omitempty"`
}

Derivation is one pair of planned nodes and the designer's verdict on whether one runs downstream of the other. A and B are node ids in the program the same envelope carries; Why is the one line that makes the verdict an argument rather than an assertion — the data that flows, or the reason none has to.

func PairsWithin

func PairsWithin(h Harness, pairs []Derivation) []Derivation

PairsWithin is a derivation table narrowed to the pairs the given program can still be held to: every pair both of whose ids are nodes it contains.

IT EXISTS FOR THE REVIEW PASS AND FOR NOTHING ELSE. A critic patches a draft with ops and never restates the table, so the DRAFT's table is what the patched page is checked against — and the ordinary patch, dropping a node that earned nothing (OpDropNode), leaves that table naming a job the page no longer has. Held as it stands, the table refuses the page and the whole review is thrown away by the designer's own homework about an earlier draft.

This is not a way around the check. A pair that is dropped names a node that does not exist, and CheckDerivation already holds that a pair NOT in the table makes no claim at all; every pair over nodes that survived the patch is still checked against every edge. Nil out means no claim, which is what an absent table has always meant.

type Dyn

type Dyn struct {
	Ladder string `json:"ladder"`
	Cap    int    `json:"cap,omitempty"`
}

Dyn is how much shape a run may decide for itself, and the budget it may spend deciding. Cap is a whole-run allowance, not a per-node one: an extra loop round costs a unit, a minted branch of width costs a unit, and a run that spends its last unit stops deciding and finishes on the shape it has.

func (*Dyn) UnmarshalJSON

func (dyn *Dyn) UnmarshalJSON(data []byte) error

UnmarshalJSON gives the dynamism budget the same narrow hand-written form as version without making the rest of the page permissive.

type Edge

type Edge [2]string

Edge is a directed dependency, from node id to node id. Edges are ids and not indices because a program is edited by hand and by model, and an index silently means a different node the moment a line moves.

func (Edge) From

func (e Edge) From() string

From names the edge's source node.

func (Edge) String

func (e Edge) String() string

func (Edge) To

func (e Edge) To() string

To names the edge's target node.

type Entry

type Entry struct {
	// Name is the harness's id, its filename, and the word the offer card says
	// out loud: "research".
	Name string

	// Description is one sentence saying what this harness does, in the words a
	// person would use for it — not in the words the program uses for itself.
	// It is shown under the offer, and its content words are the second of the
	// two matching signals (detect.go).
	Description string

	// Cues are the words and phrases this harness answers to: ["research",
	// "find out", "dig into"]. They are the designer's own trigger vocabulary,
	// frozen at build time, which is what makes detection a table lookup rather
	// than a judgement — see detect.go for why that matters more than recall.
	//
	// A cue of several words matches only as a PHRASE, and counts for more than
	// a single word does: "find out" in a sentence is evidence, "find" on its
	// own is a coincidence waiting to happen.
	Cues []string

	// Revision pins the version this entry is: v1, v2, an integer that only
	// ever goes up (docs/SUBHARNESS.md). Detection does not read it — it is
	// here because an entry without it is not an entry, and a surface that
	// offers a harness should be able to say which one it offered.
	Revision int
}

Entry is one registry entry, in the fields detection reads. The rest of an entry — the program, the tool whitelist, the verify ladder, the dynamism cap, the run history — is the engine's and lands beside these without touching them.

DESCRIPTION AND CUES ARE WRITTEN AT BUILD TIME, by whoever designs the harness, and they are what makes it findable without a slash command. That is the whole bargain of this package: a person says what they want in their own words, and a harness that was described honestly is offered. A harness described as "does stuff" is never offered, and that is the designer's answer to receive, not a defect for the matcher to paper over.

type Env

type Env interface {
	// Loop runs one agent.loop node on input and returns what the worker
	// produced.
	Loop(ctx context.Context, node Node, input string) (string, error)
	// Tool calls one tool with the node's fixed arguments.
	Tool(ctx context.Context, node Node, input string) (string, error)
	// Gate asks a person the node's question and returns their answer. An
	// implementation with nobody to ask must return an answer rather than block
	// forever — see [GateAnswer] for what "nobody is there" should mean.
	Gate(ctx context.Context, node Node, state State) (GateAnswer, error)
	// Check runs one verify node. The bool is whether it passed; the string is
	// what it said, which becomes the state the next condition reads. An ERROR
	// means the check could not be run at all, which is a different fact from a
	// check that ran and failed.
	Check(ctx context.Context, node Node, state State) (bool, string, error)
	// Cond judges a branch's `when` or a loop.until's `until` when the condition
	// language cannot (predicate.go). It is the seam where "the suite is green"
	// stops being a string and becomes an answer, and it is asked ONLY for
	// conditions [ValidCondition] refuses — everything the small language can
	// decide is decided here, in this package, the same way every time.
	Cond(ctx context.Context, node Node, condition string, state State) (bool, error)
}

Env is everything the runner cannot do by itself. Every method may block and every method must respect its context.

type Exec

type Exec func(context.Context, Node) (Result, error)

Exec runs one node. It is the whole interface between a sub-harness and the machinery that does the work.

func ModelExec

func ModelExec(c *provider.Client, opts ModelExecOpts) Exec

ModelExec turns a provider client into the executor Run wants.

The returned Exec is SINGLE-USE per run: it accumulates the outputs the run has produced, which is what a later node's brief and a verify's evidence are built from. Running two harnesses through one Exec would thread the first one's outputs into the second, so a second run takes a second Exec.

type Fields

type Fields map[string]string

Fields are one node's arguments. They are strings on the wire — including the integers — so that a saved page round-trips byte for byte and the integer caps are enforced in one place (Int, below) rather than by whatever numeric type a decoder happened to choose.

func (Fields) Get

func (f Fields) Get(name string) string

Get reads a field with its surrounding whitespace removed.

func (Fields) Int

func (f Fields) Int(name string, fallback int) int

Int reads an integer field, returning fallback when the field is absent or blank. It is total on purpose: Validate has already refused every page whose integer fields do not parse, so the runner reads them without a second error path.

func (*Fields) UnmarshalJSON

func (f *Fields) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts the one liberty a designer takes with a node's arguments: scalars spelled the JSON way - "max_turns": 6 rather than "6". Numbers and booleans are coerced to their canonical string form, so the map stays strings on the wire and Encode keeps one spelling. Objects, arrays, and null are refused with the field named.

type GateAnswer

type GateAnswer struct {
	Approved bool
	// Intervene means the person is taking over: the run ends as intervened,
	// whatever Approved says.
	Intervene bool
	// Note is what they typed — a redirect, a reason, or the instruction they
	// are continuing with.
	Note string
}

GateAnswer is what a person said at a human.gate.

THE THIRD ANSWER IS THE POINT. Approve and decline are the two a countdown card already has; intervene is the one a harness needs, because 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 the run stops where it stands, its trace is complete up to that node, and the words they typed become the run's output for whoever picks it up.

func (GateAnswer) Word

func (g GateAnswer) Word() string

Word is the answer in the one word the trace records.

type Harness

type Harness struct {
	Id        Id       `json:"id"`
	Program   Program  `json:"program"`
	Whitelist []string `json:"whitelist,omitempty"`
	Verify    Verify   `json:"verify"`
	Dyn       Dyn      `json:"dyn"`
	Tests     []Test   `json:"tests,omitempty"`
}

Harness is one registry entry, whole. It is the unit the store versions and the unit the runner runs.

func Apply

func Apply(h Harness, ops []Op) (Harness, error)

Apply is the patch, applied. It is PURE: the harness handed in is not touched, including its maps and slices, so a caller can print the draft beside the revision afterwards.

The error is the LAW's, not an op's — failed ops are skipped and reported (see ApplyReport), and what comes back as an error is Validate refusing the result. A patch that would produce a page this package will not load is a failed review, and the critic is told so in the validator's own words.

func Decode

func Decode(data []byte) (Harness, error)

Decode reads a saved page. The wire format is strict JSON with unknown fields refused: a page is written by this package and by the distiller, and a field neither of them knows is a version skew worth an error rather than a silently dropped instruction.

func (Harness) Allows

func (h Harness) Allows(tool string) bool

Allows reports whether a tool is on the harness's whitelist. An empty whitelist allows nothing — a harness that wants a tool has to say which one.

func (Harness) Normalize

func (h Harness) Normalize() Harness

Normalize fills the two defaults a file may leave out. An omitted rung means the least of that ladder — believe the output, decide nothing — because the safe reading of silence is the timid one.

type Hosted

type Hosted struct {
	// Harness and Version identify the entry, pinned. The source stores the
	// version it mounted so a run started from this command is the harness the
	// person read about, not whatever was saved since.
	Harness string `json:"harness"`
	Version int    `json:"version"`

	// Command is the line, always `/harness <name>`.
	Command string `json:"command"`

	// AllowedArgs is the trigger's whitelist, copied so the source can complete
	// and refuse arguments without reading the harness.
	AllowedArgs []string `json:"allowed_args,omitempty"`

	// Entry is the node the command enters at — the trigger's successor — and
	// Node is the trigger itself, so a run can be attributed to the line that
	// started it.
	Entry string `json:"entry"`
	Node  string `json:"node"`

	// Desc is the harness's own one-liner, for whatever help the source renders.
	Desc string `json:"desc,omitempty"`
}

Hosted is one hosted trigger as the source receives it: everything needed to mount the command and route it back, and nothing about how the harness runs.

func Host

func Host(src Source, h Harness) ([]Hosted, error)

Host offers every hosted trigger in a harness to a source and returns what was mounted. The harness is validated first: a source should never be handed a command whose harness would refuse to run, because the failure would surface as a broken command line long after the file was written.

A harness with no hosted trigger mounts nothing and is not an error — plenty of them are started by a watch, by idle, or by another harness calling them, and none of those should have to say so.

func (Hosted) Accepts

func (h Hosted) Accepts(args []string) error

Accepts checks a whole argument list and names the first word that is not allowed. It is the source's refusal, written once here so every source refuses the same way and with the same sentence.

func (Hosted) Allows

func (h Hosted) Allows(arg string) bool

Allows reports whether one argument word is on the whitelist. A `name=value` or `--name=value` word is judged by its name: the whitelist is about which arguments exist, and a value is the caller's business.

type Id

type Id struct {
	Name    string `json:"name"`
	Desc    string `json:"desc,omitempty"`
	Author  string `json:"author,omitempty"`
	Version int    `json:"version"`
}

Id is who a sub-harness is. Version is the pointer: 1, 2, 3, minted by the store, never chosen by the writer.

func (*Id) UnmarshalJSON

func (id *Id) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts a hand-written string for the identity's integer while preserving the page's refusal of fields this version does not know.

type Kind

type Kind struct {
	Name   string
	Desc   string
	MinDyn string
	Specs  []spec
	Valid  func(Fields) error
}

Kind is one registered node kind. MinDyn is the lowest dynamism rung a harness may declare and still contain this kind; Specs are the kind's fields as DATA — the validator is built from them and the designer's guide is rendered from them, so the law the model is told and the law the page is held to cannot drift apart. Valid is the kind's own law over its fields; nil means the specs are the whole law and Register builds it.

func Kinds

func Kinds() []Kind

Kinds lists the registry in name order.

func Lookup

func Lookup(name string) (Kind, bool)

Lookup finds a registered kind.

type LastRun

type LastRun struct {
	// At is when the run happened. A zero time is a program nobody has run, and
	// it is the one case that draws nothing at all.
	At time.Time
	// Finished is whether the run reached the end it promised.
	Finished bool
	// CostUSD is what it cost, and zero is "nobody said" rather than "free".
	CostUSD float64
}

LastRun is the whole of what a list row needs about a run: when it was, how it ended, and what it cost. It is a reading rather than a record — the record is a trace beside the page (store.go) or a note beside the bundle (internal/substore's runs.go), and this is what both of those come back as.

func TraceRun

func TraceRun(t Trace) LastRun

TraceRun is a saved trace read as that reading.

ONLY `ok` IS FINISHED. The other four statuses (exec.go) are a person saying no at a gate, a person taking the run over, a context that died, and a node that failed — none of which reached the end the program promised, and all of which are `incomplete` in the row's own two words.

The cost is deliberately absent: a trace records the walk and not the ledger, so a row drawn from one says when and how and stops there.

type Loader

type Loader interface {
	Load(name string, version int) (Harness, error)
}

Loader is how a subharness.call reaches another program. *Store satisfies it; a test can satisfy it with a map.

type Match

type Match struct {
	Entry Entry
	Score float64
}

Match is one entry and what it scored.

func Best

func Best(turn Turn, entries []Entry) (Match, bool)

Best is the highest-scoring entry and whether it clears the bar. The bool is the whole decision a surface needs: true means raise the card, false means this was an ordinary turn.

Two ways to clear it — a score that stands alone, or a lead nothing else in the registry comes near — and one line under both (see Threshold).

Ties go to the entry that comes FIRST in the registry — the comparison is strictly greater — so a build whose registry loads in a fixed order asks the same question twice for the same sentence. Two entries that score the same are also, by construction, not a clear winner: a sentence that describes two harnesses equally well is a sentence nobody should be asked about.

type ModelExecOpts

type ModelExecOpts struct {
	// Harness is the program being run, and it is REQUIRED. An [Exec] is handed
	// one Node at a time and can see nothing around it, so the shape a node sits
	// in — which harness's whitelist bounds it, which nodes lead into it — has
	// to be closed over here.
	Harness Harness

	// RunTool calls one whitelisted tool with the node's fixed arguments. Nil
	// means this surface has no tools, and a tool.call under it FAILS rather
	// than returning nothing: a program that asked to run the suite and was
	// quietly told "" would go on to verify that "" looks fine.
	RunTool func(ctx context.Context, tool, args string) (string, error)

	// Toolbelt is what an agent.loop that NAMES tools is handed. A zero one is a
	// surface that can run a tool.call and not a tool loop, and a node under it
	// is told as much rather than being offered a belt that goes nowhere.
	Toolbelt Toolbelt

	// Ask puts a human.gate's question to a person and returns what they typed.
	// Nil is nobody there — see the note at the top of this file.
	Ask func(ctx context.Context, question string) (string, error)

	// MaxTurnsDefault is what an agent.loop that declares no max_turns is told
	// it was budgeted. Zero takes this file's own default.
	MaxTurnsDefault int

	// Store, when set, is where a subharness.call resolves the harness it names
	// and where that child's own trace is saved. Nil means this bridge cannot
	// make calls, and a program that tries says so.
	Store *Store

	// Model is what THIS RUN was asked to think with: the default model for
	// every agent.loop node that did not pin one of its own. Empty is the
	// ordinary run, on the client's model. exec_model_model.go holds the rule
	// and says why a node's own `model` still wins.
	Model string

	// Usage, when set, is the ledger every model call this run makes is folded
	// into — the run's own bill, which is a thing no caller can reconstruct
	// afterwards because the calls are this file's and the trace records their
	// outputs rather than their cost (usage.go).
	//
	// It is a pointer the CALLER allocates and reads after the run, rather than
	// a figure on the trace, because a trace is saved to disk and a saved run's
	// price is not the same claim as what this process just spent: the trace
	// outlives the session, and a number in it would be read back tomorrow as
	// though somebody had paid it again. Nil is a caller that is not counting.
	Usage *Usage

	// DepthCap bounds subharness.call nesting: how many harnesses deep a call
	// may go below the one this bridge was built for. Zero takes
	// [MaxCallDepth]. It is a caller's knob rather than only the constant
	// because the surface driving the bridge is what knows how much recursion
	// it is willing to pay for — a chat turn and an unattended runner are not
	// the same appetite.
	DepthCap int
	// contains filtered or unexported fields
}

ModelExecOpts is everything the bridge cannot do with a model alone.

type Node

type Node struct {
	Id     string `json:"id"`
	Kind   string `json:"kind"`
	Fields Fields `json:"fields,omitempty"`
}

Node is one step of a program: an id nobody else in the program carries, a registered kind, and that kind's fields.

type Op

type Op struct {
	// Op is the verb, one of the constants above.
	Op string `json:"op"`
	// Node is the node id the edit lands on — or, for an edge, its source.
	Node string `json:"node,omitempty"`
	// Field is which field is being written, where the verb needs telling.
	Field string `json:"field,omitempty"`
	// Text is the new value — or, for an edge, its target.
	Text string `json:"text,omitempty"`
	// NodeJSON is a whole node, for add_node, in the page's own node shape.
	NodeJSON json.RawMessage `json:"node_json,omitempty"`
}

Op is one edit. The fields are a flat set rather than a union per op because the writer is a model: a flat shape is one thing to explain, one thing to validate, and a misfilled field fails as a skipped op with a sentence saying which field, rather than as an envelope that will not decode at all.

func (Op) String

func (o Op) String() string

String is how an op is printed in a review's delta: short enough for a line, specific enough that a person can see what was done without the page.

type OpResult

type OpResult struct {
	Op  Op
	Err error
}

OpResult is what became of one op. An op that failed is a REPORT and not an error: the review's other findings are still worth having, and a critic that named a node it had already dropped has made a bookkeeping mistake, not an argument that should cost the whole turn.

func (OpResult) Applied

func (r OpResult) Applied() bool

Applied reports whether this op landed.

type Program

type Program struct {
	Nodes []Node `json:"nodes"`
	Edges []Edge `json:"edges,omitempty"`
}

Program is the DAG. Node order is the tie-break everywhere order matters — the entry search, a branch's default successor, the topological walk — so a program that is rewritten with its nodes in the same order runs the same way twice.

func (Program) Node

func (p Program) Node(id string) (Node, bool)

Node finds a node by id.

func (Program) Predecessors

func (p Program) Predecessors(id string) []string

Predecessors lists the ids that lead to id, in edge order.

func (Program) Successors

func (p Program) Successors(id string) []string

Successors lists the ids an edge leads to from id, in edge order.

type Result

type Result struct {
	// Out is the node's output, condensed for the trail. The full artifact
	// belongs wherever the executor keeps artifacts; what lands here is what a
	// person reading the run needs to see.
	Out string
	// Next is a branch node's choice: the id of the one successor to take.
	// Empty means the first successor in program order. Ignored by every other
	// kind.
	Next string
	// Done is a loop.until node's condition: true when the loop may stop.
	// Ignored by every other kind.
	Done bool
}

Result is one node's outcome, as its executor reports it.

type RunPage

type RunPage func(ctx context.Context, name, text, model string, step func(Trail)) (string, Usage, error)

RunPage is what actually runs a page: the name, the request in the person's own words, the model to think with, and a look at each step as it lands. It is the SAME function the conversation already runs a page with (internal/session's Config.RunHarness), handed here rather than reimplemented, so a run started from a list and a run started by a turn are one run through one path.

The model may be empty, and that is what every run started from a list passes: empty means the model the runner was built on, which is the conversation's own.

type Runner

type Runner struct {
	// Env is what makes the nodes do anything. Required.
	Env Env
	// Loader resolves subharness.call. Nil means this runner cannot make calls,
	// and a program that tries gets an error rather than a silent skip.
	Loader Loader
	// Saver, when set, is where a called harness's own trace is written.
	Saver Saver
	// Depth is how many calls deep this runner already is. The top level is 0.
	Depth int
}

Runner gives a harness's nodes their meaning and hands the shape to Run. It is single-use per Run call and holds no state between them.

func (*Runner) Run

func (r *Runner) Run(ctx context.Context, h Harness, input string) (Trace, error)

Run executes a harness and returns its trace. The error is non-nil only for a run that FAILED or was cancelled; a declined or intervened run comes back with a complete trace and no error, because nothing went wrong.

The walk is Run's, unchanged — this method only decides what each node means and what the run's last word is.

type Salvaged

type Salvaged struct {
	// JSON is text encoding/json has already agreed to read.
	JSON []byte
	// Rung is the name of the step that produced it — [SalvageStrict] when the
	// reply needed nothing, which is the only outcome worth being quiet about.
	Rung string
	// Climbed names every rung attempted, in order, ending at Rung.
	Climbed []string
}

Salvaged is what a successful walk up the ladder leaves behind: the JSON, the rung that produced it, and the rungs that were spent getting there.

func SalvageDetail

func SalvageDetail(raw string) (Salvaged, error)

SalvageDetail is Salvage with the rung it succeeded at, for callers that log what the reply cost them.

func (Salvaged) Clean

func (s Salvaged) Clean() bool

Clean reports whether the reply arrived needing no repair at all.

type Saver

type Saver interface {
	SaveRun(Trace) (string, error)
}

Saver is where a called harness's own run is recorded. A call's child run belongs in the CHILD's history — that is where somebody looking at "how has triage behaved" would go — so the parent's trace records the path and the child's file holds the detail. *Store satisfies this too.

type Source

type Source interface {
	AddTrigger(Hosted) error
}

Source is anything that can host a command: a chat surface, the TUI, an external command source. One method, because that is the entire seam — the source learns a command exists and where to send it, and nothing else about sub-harnesses at all.

type State

type State struct {
	Last string
	OK   bool
}

State is what a condition is asked about: the last step's output, and whether that step succeeded. It is one struct rather than two arguments because the runner threads it through every arm and a positional (string, bool) pair reads identically whichever way round it is wrong.

type Status

type Status string

Status is how a run ended. It is written into the trace so a saved run says what happened without the reader re-deriving it from an error string.

const (
	// StatusOK means the program reached its end.
	StatusOK Status = "ok"
	// StatusFailed means a node failed and nothing caught it.
	StatusFailed Status = "failed"
	// StatusDeclined means a person said no at a human.gate. It is NOT a
	// failure: the gate did exactly what it is for, and a history that filed
	// every refusal as a fault would be a history that punishes the feature.
	StatusDeclined Status = "declined"
	// StatusIntervened means a person took the run over at a gate — the
	// escalation ([GateAnswer.Intervene]). The program stopped where it stood
	// and a human continued from there.
	StatusIntervened Status = "intervened"
	// StatusCancelled means the context died: an interrupt, a closed session, a
	// deadline.
	StatusCancelled Status = "cancelled"
)

type Store

type Store struct {

	// Now stamps a run file. It is a field so a test can run two harnesses in
	// the same second and still get two names it chose.
	Now func() time.Time
	// contains filtered or unexported fields
}

Store is the durable registry at one directory. It holds no cache: a harness page is small, read rarely, and edited by hand often enough that a stale read would be the more expensive mistake.

func At

func At(dir string) *Store

At opens the registry at a directory. The directory is created on first write, not here, so listing a machine that has never saved a harness is not itself a mutation.

func Default

func Default() *Store

Default opens the registry codeaf owns: ~/.codeaf/harnesses, moved wholesale by CODEAF_HOME like everything else durable.

func (*Store) Dir

func (s *Store) Dir() string

Dir names the registry directory.

func (*Store) Head

func (s *Store) Head(name string) (int, error)

Head is the highest version present.

func (*Store) HostAll

func (s *Store) HostAll(src Source) ([]Hosted, error)

HostAll offers every registered harness's head version to a source. It is the boot path: a surface that mounts commands asks once, and gets the registry as it stands on disk rather than as it stood when the process started.

A harness that cannot be read or does not validate is SKIPPED rather than failing the whole mount, because one broken page must not be able to take every other harness's command off the surface with it.

func (*Store) LastTrace

func (s *Store) LastTrace(name string) (Trace, bool)

LastTrace is the newest saved trace for one name, and false for a page nobody has run.

IT IS THE PAGE STORE'S ANSWER TO "WHEN DID THIS LAST RUN", and it is the whole answer for a page: every run of one goes through the surface's single run door (cmd/codeaf's v3RunHarness), whichever list started it, and that door saves a trace here. So a page run from `/harness` and a page run from `/subharness` both land in this directory, which is what lets the two doors agree.

A TRACE THAT CANNOT BE READ IS NO TRACE. A row asking when something last ran is not the place somebody learns their disk is broken, and the emptiness law says a row with nothing to report reports nothing.

func (*Store) Load

func (s *Store) Load(name string, version int) (Harness, error)

Load reads a version pointer. Version 0 means the head — the highest page present — and any other integer means that page and only that page, which is what makes a pin durable: v1 reads the same bytes after v2 is saved.

func (*Store) LoadRun

func (s *Store) LoadRun(path string) (Trace, error)

LoadRun reads one saved trace back.

func (*Store) Names

func (s *Store) Names() ([]string, error)

Names lists every harness in the registry, in name order.

func (*Store) Run

func (s *Store) Run(ctx context.Context, name string, version int, exec Exec) (Trace, string, error)

Run loads a version pointer, runs it, and saves the trace under the harness's run directory. The trace is saved whether the run succeeded or not, and its path comes back with it.

func (*Store) Runs

func (s *Store) Runs(name string) ([]string, error)

Runs lists a harness's saved traces, oldest first — which is filename order, because the stamp was chosen to make those the same thing.

func (*Store) Save

func (s *Store) Save(h Harness) (Harness, error)

Save mints the next version and writes it. It is the only way a page is written, and it validates first: an invalid harness never reaches the disk, so every page a reader finds is one the runner can run.

The version is minted, never chosen. A caller that already knows which version it means may say so — h.Id.Version — and Save refuses if the disk disagrees, which is how a distiller that read v2, thought about it, and came back to save finds out that someone else saved v3 in the meantime.

func (*Store) SaveRun

func (s *Store) SaveRun(t Trace) (string, error)

SaveRun writes one run's trace under the harness's run directory and returns the path it landed at. Traces are kept whole rather than folded into a log because the run is the evidence: the dynamism ladder is only honest if the output-trace of what a run actually decided survives the run.

func (*Store) Source

func (s *Store) Source(run RunPage) exec.BundleSource

Source is this store as a place the registry loads programs from.

func (*Store) Versions

func (s *Store) Versions(name string) ([]int, error)

Versions lists a harness's pages in ascending order. A harness that was never saved has no versions and is not an error — asking is how a caller finds out.

type Test

type Test struct {
	Name   string `json:"name"`
	Input  string `json:"input,omitempty"`
	Expect string `json:"expect,omitempty"`
}

Test is one case the harness is expected to survive. It is held here, beside the program, so that a version pointer names the shape and the cases that justified it together.

type Toolbelt

type Toolbelt struct {
	// Defs is every tool this surface can run, in the shape the wire wants. A
	// node is offered only the ones it named; the rest never leave this struct.
	Defs []ai.ToolDefinition

	// Call runs one of them on the arguments the model wrote, and answers with
	// the tool's own text — the same thing RunTool answers with. An error is the
	// tool refusing, and the loop hands it back to the model rather than failing
	// the node, because reacting to a tool that said no is what a loop is FOR.
	Call func(ctx context.Context, tool string, args map[string]any) (string, error)
}

Toolbelt is the LOOP half of tool access: the definitions an agent.loop may be offered, and where the arguments the MODEL wrote go when it asks for one.

IT IS ONE FIELD BECAUSE IT IS ONE CAPABILITY. Definitions with no dispatcher is a belt that can be advertised and never run; a dispatcher with no definitions is a belt nothing can ask for. Either half on its own is a bridge that lies to the model about what it can reach, so they arrive together or not at all — and a zero Toolbelt is the same honest absence a nil ModelExecOpts.RunTool already is: the step is told its tools are named and not callable, and makes one completion.

IT IS NOT ModelExecOpts.RunTool AND DOES NOT REPLACE IT. A tool.call is the FIXED half of the library (kinds.go) — its arguments are the page's own and no model ever touches them — so it keeps its own closure with its own fixed-string signature. A surface wires both to the same execution path, which is what makes a tool reached from a loop and a tool reached from a tool.call the same tool.

type Trace

type Trace struct {
	Id      Id            `json:"id"`
	Started time.Time     `json:"started"`
	Elapsed time.Duration `json:"elapsed"`
	Trail   []Trail       `json:"trail"`
	Edges   []Edge        `json:"edges,omitempty"`
	// Spent is how much of Dyn.Cap the run used. A run that spent nothing took
	// the shape the program already had.
	Spent int `json:"spent,omitempty"`
	// Err is the failure that ended the run early, if one did. It is recorded
	// rather than only returned because a saved trace outlives the call.
	Err string `json:"err,omitempty"`
	// Status is how the run ended in one word (exec.go). The walk fills in the
	// two it can tell apart on its own — ok and failed — and a [Runner] refines
	// it with the three that are somebody else's news: a person declined at a
	// gate, took the run over, or the context died. It is a field rather than a
	// reading of Err because "a person said no" is not a failure, and a history
	// that could only spell it as one would punish the gate for working.
	Status Status `json:"status,omitempty"`
	// Out is the run's own output: what the last node left behind, as the
	// executor condensed it. The trail holds every step's output; this is the
	// one the caller was waiting for.
	Out string `json:"out,omitempty"`
}

Trace is one run, whole: who ran, what executed, which edges were taken, and what the run spent of its dynamism budget.

func Run

func Run(ctx context.Context, h Harness, exec Exec) (Trace, error)

Run walks a harness's program, handing each reached node to exec.

The rules are the program's own. A branch takes one successor and everything only that branch led to is never reached. A parallel.join in `all` mode waits for every incoming edge and is skipped if a branch upstream means one will never arrive. A loop.until re-executes the same node until its executor says Done, bounded by both its own max_rounds and what is left of the harness's dynamism budget — the run stops deciding when the budget is gone, it does not fail.

A failing exec ends the run: the failure is recorded in the trail, in the trace, and returned. The trace comes back either way, because a run that died halfway is exactly the run worth reading.

func RunFile

func RunFile(ctx context.Context, path string, exec Exec) (Trace, error)

RunFile runs a harness page read straight off disk, without going through the registry. This is how a draft is tried before it is saved.

func RunWatched

func RunWatched(ctx context.Context, h Harness, exec Exec, watch func(Trail)) (Trace, error)

RunWatched is Run with somebody looking over its shoulder: watch is handed each step the instant it lands in the trail, including the one a failure ends the run on.

IT IS THE SAME TRAIL ENTRY THE TRACE KEEPS, and that is the whole point. A run takes minutes and a person watching one has, until now, seen the announcement and then nothing until the report — so a surface needs the steps as they happen. Handing it anything but the trail's own entry would be a second account of the same step, free to disagree with the card read back afterwards.

The walk does not care whether anybody is watching: a nil watch is the ordinary run, and a watch that blocks blocks the run, which is the caller's business to avoid.

type Trail

type Trail struct {
	Step    int           `json:"step"`
	Id      string        `json:"id"`
	Kind    string        `json:"kind"`
	Out     string        `json:"out,omitempty"`
	Err     string        `json:"err,omitempty"`
	Elapsed time.Duration `json:"elapsed"`
}

Trail is one executed step, condensed. A loop that ran three rounds leaves three entries with the same Id and three different Steps, which is the only honest way to record a bounded loop in a flat list.

type Turn

type Turn struct {
	Text string
}

Turn is what a person just said, and the whole of what detection reads. It is a type rather than a string so that the day detection wants a second fact — the turn before it, whether an image rode along — the signature does not change under every caller.

type Usage

type Usage struct {
	// Model is what the run ran on as the PROVIDER reported it, and it is empty
	// when the calls did not agree — a run whose nodes pinned models of their
	// own is not a run one name describes, and picking one of them would be
	// this package stating a fact nobody gave it.
	Model string

	// Calls is every request the run 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

	// Input and Output are the provider's token counts, summed in the same
	// spelling the session's own accounting keeps them: Input is prompt tokens
	// as reported, with cache reads beside it rather than inside it.
	Input  int
	Output int

	// CacheRead and CacheWrite are the prompt-cache accounting, read through
	// ai.Usage's accessors so that both dialects — Anthropic-native and the
	// OpenAI-style nesting — land in the same field.
	CacheRead  int
	CacheWrite int

	// CostUSD is the provider's OWN figure, summed. Zero is "the provider did
	// not say", which is not the same fact as "free" — a caller that wants a
	// price for a quiet provider has to derive one from the tokens, and a
	// caller that cannot show nothing (the emptiness law).
	CostUSD float64
	// contains filtered or unexported fields
}

WHAT A RUN COST, which is a figure only this package is in a position to add up.

A run is one request per verify, one per free-text condition, and a whole bounded tool loop per agent.loop — dozens of calls a surface never sees, because what comes back to it is a trail of outputs and a saved trace. Until this ledger existed the money was not merely unwired anywhere: nothing captured it, so a harness RUN was free to every cost surface in the program while DESIGNING one was billed to the session that asked for it.

It is the SUM and not a list of calls, because the door it goes through on the other side takes one set of figures and a call count (internal/session's addAuxiliaryUsage). The per-call detail that matters to a person reading a run afterwards is the trail, which this does not replace.

NOTHING HERE LOCKS, and that is the walk's law rather than an omission: run.go hands the executor one node at a time, and a called harness runs inside the node that called it, so every fold below happens on the one goroutine the run has. An executor that ran lanes concurrently would own this ledger's safety along with everything else it changed about the walk.

func (Usage) Reported

func (u Usage) Reported() bool

Reported says whether the provider gave this run any accounting at all.

It is the question a caller asks before billing somebody: a run of ten calls on an endpoint that publishes no usage leaves every field zero, and folding that into a session total would be this build claiming a run was free.

type Verify

type Verify struct {
	Ladder string `json:"ladder"`
}

Verify is how hard a run has to work to be believed.

Directories

Path Synopsis
Package prompts is where the designer's brief LIVES, as prose, so that the hardest writing in this system is edited as writing rather than as a builder full of Fprintf calls.
Package prompts is where the designer's brief LIVES, as prose, so that the hardest writing in this system is edited as writing rather than as a builder full of Fprintf calls.

Jump to

Keyboard shortcuts

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