Help us shape the future of Go! Take the 2026 Go Developer Survey

runview

package
v0.25.0 Latest Latest
Warning

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

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

Documentation

Overview

Package runview is a read-only, local web viewer for a journal's runs: a run list (RunList) with each run's kind and state, and one run's page: a plan flow's declared topology with the run's path drawn over it, or an agent run's timeline of model turns, tool calls, attempts, approvals and halts, with the audit verdict when a signed head or evidence package is given. The page of a run in progress, read from a store, follows the run as records land (see Follower). It never writes to the store it reads, and it serves on a loopback address only (see Listen). The bide view command (cmd/bide, its own module) is its front end.

Index

Constants

View Source
const (
	AttentionApproval = "approval" // a call waits for approvers
	AttentionHalt     = "halt"     // an operation's outcome is unknown
	AttentionAborted  = "aborted"  // the run aborted (a saga rolled back)
	AttentionRecovery = "recovery" // recovery has kept failing for the run (from the notify trail)
)

Attention item kinds.

View Source
const (
	NotifyNone     = "none"       // no notifier recorded the event
	NotifyObserved = "observed"   // seen, not yet delivered
	NotifyTried    = "delivering" // delivered at least once, not acknowledged
	NotifyFailing  = "failing"    // delivery has failed repeatedly
	NotifyAcked    = "acked"      // the sink acknowledged
	NotifyCleared  = "cleared"    // the notifier stopped: resolved, decided or ended
)

Notify states, in the order a delivery goes through them.

View Source
const (
	SourceRegistry    = "registry"
	SourceDescription = "description"
)

Source says what a typed result rests on: "registry" (the program's plan.Registry), "description" (a description file, not checked against the program), or "" (structural).

View Source
const (
	StatusAttention = "attention"
	StatusClear     = "clear"
	StatusUnknown   = "unknown"
)

A page's status in the JSON, from the same Verdict as its headline: attention when something needs a person, clear when the page read everything and found nothing, unknown when it cannot say.

View Source
const (
	MaxTopologyNodes = 1000
	MaxTopologyEdges = 4000
)

MaxTopologyNodes and MaxTopologyEdges bound the topology a diff against a journaled run reads: its nodes, and its edges plus Switch arms. The loader bounds a config by bytes only, and the diff costs more than linear time (about 0.66 s at 1000 nodes with disjoint names, 3.2 s at 4000), so a topology a journal supplies is refused past these.

View Source
const (
	DBFlagHelp          = "SQLite journal file to read, opened read-only (sqlite.OpenReadOnly)"
	PostgresFlagHelp    = "" /* 146-byte string literal not displayed */
	PostgresEnvFlagHelp = "" /* 197-byte string literal not displayed */
	JournalFlagHelp     = "exported journal (audit.ExportJournal as JSON) to read; repeatable"
)

The help text of the flags that name a store, shared by every command that reads one (bide view, bide edit), so the two cannot drift.

View Source
const (
	ThemeFlagHelp        = "" /* 332-byte string literal not displayed */
	ThemeDefaultFlagHelp = "" /* 159-byte string literal not displayed */
)

The help text of the -theme and -theme-default flags of bide view and bide edit.

View Source
const DescriptionNote = "" /* 174-byte string literal not displayed */

DescriptionNote is what a typed result from a description file is labelled with. A description is data a person or a build wrote: it can be stale or wrong, so a gate or a type it states is not something the program was asked about. The check made with the program's Registry (plan/planview) is the control.

View Source
const KindSteps = "steps"

KindSteps is the kind of a run journaled by Journal.Step calls alone: it has no run:start, so the journal does not say what kind of run it is.

View Source
const MaxDescriptionFile = 1 << 20

MaxDescriptionFile caps a description file: it is read whole, and a program with thousands of blocks describes itself in far less.

Variables

This section is empty.

Functions

func Assets added in v0.19.0

func Assets() fs.FS

Assets is the viewer's embedded stylesheets and theme script (style.css, graph.css, extras.css, theme.js and the rest of its assets directory), so another page in the module, the flow editor (package flowedit), draws with the same tokens and serves them from its own binary. It is read-only.

func AssetsWith added in v0.22.0

func AssetsWith(s ThemeSet) fs.FS

AssetsWith is Assets with the shipped themes and the set's custom themes in the page's style.css, the set's Default as the theme a reader starts in and its Lock as the only theme. It panics on a set that does not Validate.

func Check added in v0.14.0

func Check(w io.Writer, c Config) bool

Check writes c's problems as "file:node: message" lines (the node, "from->to" for an edge or arm, or nothing for the config as a whole), warnings marked as such, then, when the config has assertions, a line counting their verdicts, and reports whether any problem is an error (or, with c.Strict, any problem at all). A violated, undecided or invalid assertion is an error.

func CheckDiff added in v0.15.0

func CheckDiff(w io.Writer, d Diff) (weakened bool, err error)

CheckDiff writes the diff as text, for CI and a pull request comment: one line per change, a line per weakened approval gate, and a summary line with the counts and the digest, each prefixed with the new config's name. It reports whether an approval policy was weakened, or the diff could not be certified (DiffView.Fallback: a side checked against a Registry or description had errors, so the gates were compared structurally).

func CheckDiffStyled added in v0.16.0

func CheckDiffStyled(w io.Writer, d Diff, st term.Style) (weakened bool, err error)

CheckDiffStyled is CheckDiff in st's colors (bide check -diff -check on a terminal): added green, removed red, changed amber, and each weakened approval gate in bold red, called out before the changes rather than after them. With color off it writes what CheckDiff writes.

func CheckStyled added in v0.16.0

func CheckStyled(w io.Writer, c Config, st term.Style) bool

CheckStyled is Check in st's colors (bide check on a terminal): a red cross on each error, a violated assertion's counterexample path in red with the node it reaches in bold, risk classes colored as the risk map colors them, and the verdict counts in green, red and amber. With color off it writes what Check writes.

func DecodeDescription added in v0.22.0

func DecodeDescription(b []byte, name string) (*plan.RegistryDescription, error)

DecodeDescription is ReadDescription for bytes already read; name is used in errors.

func ErrClass added in v0.25.0

func ErrClass(err error) string

ErrClass names what kind of failure err is, without the error's text, which may hold a path, a DSN or a record: "timed out", "canceled", "store error" or "read error".

func GateText added in v0.19.0

func GateText(g *plan.TopologyGate) string

GateText is a declared approval gate in the words the config page, the diff and the flow editor share: "2 of 3: ops, finance, risk excluding the requester, times out after 24h" or "one decision". It is "" for no gate.

func Handler

func Handler(o Options) http.Handler

Handler serves the run list at / (as JSON at /runs.json; see RunList), the Needs attention list at /attention (as JSON at /attention.json; see AttentionList) and a run at /run?id=<run> (its View as JSON at /run.json?id=<run>, and, for a run in progress in a store, its live updates at /run/live?id=<run>&after=<cursor>). The run list and the Needs attention list are built at most once per cacheTTL (2 seconds) per query, and a build is cut off after buildTimeout (30 seconds), so many viewers cost the store one read of its runs. It answers only GET and HEAD, and only requests addressed to a loopback host, so a page on another site cannot read it through a rebound DNS name.

func Listen

func Listen(addr string) (net.Listener, error)

Listen listens on addr, which must name a loopback IP (127.0.0.1 or ::1) or "localhost" (listened on as 127.0.0.1). Any other host, an empty one included, is refused: the viewer shows a journal's contents and must not be reachable from another machine.

func Main

func Main(args []string, stdout, stderr io.Writer, stores Stores) int

Main is the bide view command: it parses args, opens the input, and serves until interrupted. It returns the exit status: 0 after a clean shutdown, 2 for a usage error, 1 otherwise.

func MarkDeadlines added in v0.18.0

func MarkDeadlines(gates []Gate, now time.Time)

MarkDeadlines sets Due and Overdue on each gate with a recorded deadline, for where it stands on now: due in so long, overdue (the gate waits past it and no drive has recorded the expiry yet), or expired. Build leaves them empty, since it reads no clock; the pages and bide approve -list call this with theirs.

func NormalizeForRun added in v0.25.0

func NormalizeForRun(r plan.ConfigReport, typed bool) plan.ConfigReport

NormalizeForRun returns r as it is compared with a run: a deep copy (a run's report is cached and shared, so the original is never changed), with Safety and Assertions removed, and every node counted as gated and every report as checked, so the config diff's path and safety verdicts, which need what a topology lacks, cannot arise. Without typed it also drops what only a Registry knows: node kinds (but a join's), types and gates.

func ReadDescription added in v0.22.0

func ReadDescription(path string) (*plan.RegistryDescription, error)

ReadDescription reads a plan.RegistryDescription from the file at path (what a program writes with json.Marshal of Registry.Describe). The file must be a regular file of at most MaxDescriptionFile bytes holding one JSON value with no field the description does not have, and the description must be valid (RegistryDescription.Validate). A description is untrusted data: this is the only way the viewer and the editor read one, and it is read once.

func ReadDescriptionFile added in v0.22.0

func ReadDescriptionFile(path string) (*plan.RegistryDescription, string, error)

ReadDescriptionFile is ReadDescription that also returns the SHA-256 of the file's bytes, in hex, which sha256sum on the same file prints: a result that rests on the file says which file it was.

func Sprite added in v0.19.0

func Sprite() string

Sprite is the hidden SVG glyph set a page references with <use href="#ic-NAME"/> (the names an Icon, Verdict or node uses). It is built from constants.

Types

type ApprovalCheck

type ApprovalCheck struct {
	OK   bool   `json:"ok"`
	Text string `json:"text"`

	// What the approver cards show; not in the JSON view. Counted and Denied are the approvers
	// whose decision counted in the recount, Ignored the reason each other decision did not, Keys
	// each approver's key fingerprint (from the approver keys given), and Times the two times of
	// each counted approver's decision, with their sanity flags (audit.ApprovalVerdict.Times).
	Counted, Denied []string                      `json:"-"`
	Ignored         map[string]string             `json:"-"`
	Keys            map[string]string             `json:"-"`
	Times           map[string]audit.DecisionTime `json:"-"`
	// Identity is what an audit found of each certificate-bound approver's decision on the gate (by
	// approver id), for the identity block: only a result here can mark a decision disputed, and only
	// a result for every one of them can make the block audited (the only green). Empty without one.
	Identity map[string]IdentityAudit `json:"-"`
}

ApprovalCheck is audit.VerifyApprovals' verdict on one gate, in words.

type Approvals

type Approvals struct {
	Keys     agent.ApproverVerifierFor
	Expected *agent.ApprovalPolicy
}

Approvals configures the approval-signature check a Prover runs: Keys resolves each approver's public key (nil: no check), and Expected, when set, is the policy the auditor expects, which the check runs against instead of the policy the gate's tally records.

type ApproverCard added in v0.16.0

type ApproverCard struct {
	Name     string
	Initials string
	// State is approved, denied or pending.
	State string
	// Verify says what is known of the signature, in words: "signature verified", "signature NOT
	// verified", "in the signed log, signature not checked" or "signature not checked". VerifyOK
	// reports the first. Empty for a pending approver.
	Verify   string
	VerifyOK bool
	Why      string // why the decision did not count, when it did not
	// Fingerprint is the approver's key as "ed25519:3b6a27bc" (the first 8 hex digits of the
	// SHA-256 of its public key), when -approver-keys gave it.
	Fingerprint string
	// DecidedAt is when the approver says they decided, by their clock, inside the signature;
	// RecordedAt is when the decision was journaled, by the submitter's clock (not signed). Both are display text
	// in UTC ("2026-10-13 12:14:03.123"), empty when the record holds none. Gap is the difference
	// between them in words, shown without judgment, and Flags the sanity notes on them
	// (audit.FlagDecisionTimes, under audit.DefaultDecisionTimeTolerance), which never change
	// the gate's outcome. Empty for a pending approver.
	DecidedAt, RecordedAt string
	Gap                   string
	Flags                 []string
	// TimesNote says why no times are shown when the approver has several signed decisions with
	// the card's outcome and their signatures were not checked: which one counted, and so which
	// decided_at is signed, is not known.
	TimesNote string
	// Identity is the identity of a certificate-bound approver's counted decision (see
	// IdentityCard), nil for a bare-key approver and for a decision no identity record shows.
	// Binding says which kind of approver this is on a gate with a certificate-bound approver:
	// "certificate-bound" or "bare key (no identity)", empty on a gate of bare keys. IdentityNote is
	// what to read instead of a signature line for a certificate-bound approver whose decision has no
	// identity card, and IdentityBad that it is a problem.
	Identity     *IdentityCard
	Binding      string
	IdentityNote string
	IdentityBad  bool
}

ApproverCard is one approver of an approval gate, as the page shows them: who, what they decided, whether their signature verified, the fingerprint of their key, and when they decided and when bide recorded it.

func (ApproverCard) IdentityWord added in v0.19.0

func (c ApproverCard) IdentityWord() string

IdentityWord is a non-final approval's identity in a word, for the card's state line.

func (ApproverCard) Plain added in v0.19.0

func (c ApproverCard) Plain() bool

Plain reports whether the card may be drawn as an ordinary approval: a bare-key approver's, or a certificate-bound approver's whose identity an audit result re-checked. A certificate-bound approval that is only final as recorded, provisional, bad or missing is never drawn in the approved (green) style.

func (ApproverCard) Style added in v0.19.0

func (c ApproverCard) Style() string

Style is the card's state class: the state, except that a certificate-bound approval that is not plain has its own: idrecorded (final as recorded, neutral) or idcounted (anything else).

type AttentionGate added in v0.17.0

type AttentionGate struct {
	Single     bool     `json:"single,omitempty"` // a one-decision gate (agent.Approve)
	Need       int      `json:"need,omitempty"`
	Of         int      `json:"of,omitempty"`
	Approvers  []string `json:"approvers,omitempty"`
	ApprovedBy []string `json:"approved_by,omitempty"`
	DeniedBy   []string `json:"denied_by,omitempty"`
	Pending    []string `json:"pending,omitempty"`
	Reapproval bool     `json:"reapproval,omitempty"`
	Status     string   `json:"status"` // the run page's gate status
	// ExpiresAt is the gate's recorded deadline, empty for a gate with none. Due says where it stands
	// on the list's clock (Gate.Due), DueSoon that it falls within dueSoonWindow (or has passed), and
	// Overdue that it has passed with no expiry recorded: the gate refuses decisions, and the run
	// records the expiry when it is next driven.
	ExpiresAt string `json:"expires_at,omitempty"`
	Due       string `json:"due,omitempty"`
	DueSoon   bool   `json:"due_soon,omitempty"`
	Overdue   bool   `json:"overdue,omitempty"`
	// Identity is the gate's approver identity, for a gate with a certificate-bound approver: what it
	// is paused for (paused: identity status unknown, ...) and the identity of each counted decision.
	Identity *GateIdentity `json:"identity,omitempty"`
}

AttentionGate is a waiting approval gate as the list shows it.

type AttentionItem added in v0.17.0

type AttentionItem struct {
	Kind     string `json:"kind"`
	KindText string `json:"kind_text"`
	RunID    string `json:"run_id"`
	// Call is the tool-use ID of the call (or the node key, or the operation's ID); Tool its name.
	Call string `json:"call,omitempty"`
	Tool string `json:"tool,omitempty"`
	// Since is when it began needing a person, by the journal's (or the trail's) clock: the gate's
	// ask time, the halted attempt's time, the abort's time, or when recovery began failing. Zero
	// when the journal does not say; such items come last.
	Since time.Time `json:"since,omitzero"`
	// Waiting is Since to the list's Now, as durText phrases it.
	Waiting string `json:"waiting,omitempty"`
	Summary string `json:"summary"`

	Gate *AttentionGate `json:"gate,omitempty"` // for an approval
	Halt *Halt          `json:"halt,omitempty"` // for a halt: its HaltRef is the ResolveHalt hint
	// Reason is why a run aborted, or the class of cause a recovery keeps failing with.
	Reason string `json:"reason,omitempty"`
	// Command is the bide approve command that records a decision on the call, when the viewer
	// knows how to write one (Options.ApproveCommand). Empty otherwise.
	Command string `json:"command,omitempty"`
	// NoCommand says why an approval has none although the viewer writes them: an id that cannot be
	// shown or pasted safely (approveline.ErrUnsafe).
	NoCommand string `json:"no_command,omitempty"`
	// CommandNote qualifies Command on a gate with certificate-bound approvers: the command signs a bare
	// key, so it is for the bare-key approvers only, and a bare-key decision does not count for the
	// certificate-bound ones. CertOnly: every pending approver is certificate-bound, so no command is shown.
	CommandNote string `json:"command_note,omitempty"`
	CertOnly    bool   `json:"cert_only,omitempty"`
	// CertCommand is the bide approve -cert command for a gate with a pending certificate-bound approver
	// (Options.ApproveCertCommand). When every pending approver is certificate-bound (CertOnly) it is the
	// only command (Command is empty): a bare-key decision does not count for them.
	CertCommand string `json:"cert_command,omitempty"`
	// Notify says whether the item was notified and acknowledged; nil without the notify trail.
	Notify *NotifyStatus `json:"notify,omitempty"`
}

AttentionItem is one thing a person has to do something about.

func (i AttentionItem) Link() string

Link is the run's page.

func (AttentionItem) SinceText added in v0.17.0

func (i AttentionItem) SinceText() string

SinceText is Since as the list shows it, or "" when the journal does not say.

type AttentionList added in v0.17.0

type AttentionList struct {
	// Status is what the headline says, for a script: attention, clear or unknown (see Coverage).
	Status string          `json:"status"`
	Items  []AttentionItem `json:"items"`
	// Coverage says what was read; the headline is clear only when it is complete.
	Coverage
	// Live: the source is a store the runs are still written to, so the page refreshes itself.
	Live bool `json:"live,omitempty"`
	// Now is the time the waits are measured to.
	Now time.Time `json:"now"`
	// Runs is how many runs were read.
	Runs  int      `json:"runs"`
	Notes []string `json:"notes,omitempty"`
}

AttentionList is the Needs attention page: every unfinished run that needs a person, and every aborted run: gates with a deadline first, the nearest first, then the rest newest first. It is built from each run's journal alone (the same View the run page shows), plus, when Options.Events is given, the run's notification trail. A load reads the runs that have no run:complete marker (a store lists them in its query), so it reads no finished run and has no cap.

func (AttentionList) Refresh added in v0.17.0

func (l AttentionList) Refresh() int

Refresh is the seconds after which the page reloads itself, 0 for a page that does not.

func (AttentionList) Verdict added in v0.17.0

func (l AttentionList) Verdict() Verdict

Verdict is the page's headline.

type Change added in v0.15.0

type Change struct {
	Change string `json:"change"` // added, removed or changed
	What   string `json:"what"`   // node, edge, entry or flow
	Node   string `json:"node,omitempty"`
	From   string `json:"from,omitempty"`
	To     string `json:"to,omitempty"`
	// Details say what changed, one item each ("block charge -> refund", "approval 1 of 3 -> 2 of
	// 3"); for an added or removed node, what it was.
	Details []string `json:"details,omitempty"`
	// Gate is how the node's approval gate changed: weakened (a set of decisions the new gate
	// accepts would not have passed the old one: the gate was removed, its need lowered, or an
	// approver added without raising the need to match), strengthened (the reverse, and not
	// weakened), or reordered (the same approvers and need in another policy order). Removing or
	// lengthening a gate's timeout is a weakening (a run waits longer, and late decisions count), and
	// adding or shortening one a strengthening (the gate fails closed sooner). Empty when the
	// gate did not change. A removed node's gate counts as removed.
	Gate string `json:"gate,omitempty"`
	// Weakening says what a weakened change weakened: "gate" (an approval policy: the gate was
	// removed, loosened, or lost its exclusion of the requester or its deadline), "path" (a node
	// newly reachable without passing an approval gate: one every path reached through a gate
	// before, whatever its safety, or a new or newly wired-in effect), "safety" (a node's safety
	// lowered toward read-only), "block" (a block changed on an ungated effect node, without the
	// Registry), "condition" (a Switch condition changed beside an ungated effect) or "assertion"
	// (an assert line removed or loosened, or kept and no longer holding). Empty for a change that
	// weakened nothing.
	Weakening string `json:"weakening,omitempty"`
	// contains filtered or unexported fields
}

Change is one difference between two flow definitions: a node, an edge or Switch arm, the entry, or the flow's name, added, removed or changed.

func (Change) String added in v0.15.0

func (c Change) String() string

String is the change as one line: "changed node charge: approval 1 of 3 -> 2 of 3 (strengthened)".

type Config added in v0.14.0

type Config struct {
	Name     string
	Data     []byte
	Registry *plan.Registry
	// Description is what the program registered, read from a file (bide check -blocks). It is only
	// as good as the file: the page and the text say so wherever they report a typed result.
	Description *plan.RegistryDescription
	// DescriptionSHA256 is the SHA-256 of the description file's bytes (ReadDescriptionFile), shown
	// where a result rests on the file, so a reviewer can tell which file was used.
	DescriptionSHA256 string
	// AllowUnusedBlocks makes a registered block the config does not use no problem
	// (plan.AllowUnusedBlocks). bide check -blocks sets it: a description is of the whole program, and
	// a flow config uses some of its blocks.
	AllowUnusedBlocks bool
	// Strict makes Check fail on a warning as well as on an error (bide view -check -strict).
	Strict bool
}

Config is a flow definition to show instead of a run: its file name (for messages), its JSON bytes (a YAML file converted to JSON), and, when the program embeds the viewer, the plan.Registry its blocks, predicates and merges are registered in. With no Registry the check is structural (see plan.InspectConfig), unless a Description of the registrations is given, which checks the same things by name (see plan.InspectConfigDescribed). A Registry wins over a Description.

func (Config) Inspect added in v0.25.0

func (c Config) Inspect() plan.ConfigReport

Inspect is plan.InspectConfig for c, against its Registry or else its Description.

func (Config) Source added in v0.25.0

func (c Config) Source() string

Source says what a typed result of c rests on: SourceRegistry, SourceDescription, or "" for the structural check.

type ConfigView added in v0.14.0

type ConfigView struct {
	Name  string `json:"name"`
	Flow  string `json:"flow"`
	Typed bool   `json:"typed"`
	// Source is what the typed result rests on (SourceRegistry, SourceDescription), empty when
	// structural.
	Source string `json:"source,omitempty"`
	// DescriptionSHA256 is the SHA-256 of the description file the typed result rests on, when it
	// rests on one read from a file.
	DescriptionSHA256 string               `json:"description_sha256,omitempty"`
	Digest            string               `json:"digest,omitempty"`
	Errors            int                  `json:"errors"`
	Warnings          int                  `json:"warnings"`
	Problems          []plan.ConfigProblem `json:"problems,omitempty"`
	Graph             *Graph               `json:"graph,omitempty"`
	Approvals         []string             `json:"approvals,omitempty"`
	CheckLabel        string               `json:"check"`
	// Risk counts the nodes by crash behavior and gate, for the summary card.
	Risk RiskSummary `json:"risk"`
	// Assertions are the verdicts on the config's assert lines, in order.
	Assertions []plan.AssertionResult `json:"assertions,omitempty"`
	// Verdict is the answer the page opens with (see configVerdict); it is not in the JSON view.
	Verdict Verdict `json:"-"`
}

ConfigView is what the config page shows.

func BuildConfig added in v0.14.0

func BuildConfig(c Config) *ConfigView

BuildConfig checks c with plan.InspectConfig and lays out its declared graph, each problem marked on the node, edge or arm it belongs to.

type Conformance

type Conformance struct {
	OK     bool     `json:"ok"`
	Method string   `json:"method"` // "plan.Conform", or "structural" (see structural)
	Source string   `json:"source"` // where the topology came from
	Diffs  []string `json:"diffs,omitempty"`
	Digest string   `json:"digest"` // "match", "mismatch", or "not compared"
}

Conformance is the verdict on whether the run followed the declared topology.

type Counts added in v0.15.0

type Counts struct {
	NodesAdded   int `json:"nodes_added"`
	NodesRemoved int `json:"nodes_removed"`
	NodesChanged int `json:"nodes_changed"`
	EdgesAdded   int `json:"edges_added"`
	EdgesRemoved int `json:"edges_removed"`
	EdgesChanged int `json:"edges_changed"`
}

Counts are a diff's changes by kind.

type Coverage added in v0.17.0

type Coverage struct {
	// Listable: the source can enumerate its runs. A store without an agent.Lister cannot, and a page
	// over it knows nothing about its runs.
	Listable bool `json:"listable"`
	// Truncated is how many listed runs were not read because a load reads at most maxSummarized.
	Truncated int `json:"truncated,omitempty"`
	// Unread is how many runs could not be read (their listing or their journal failed); Err says
	// the class of the first failure, never the store's own text.
	Unread int    `json:"unread,omitempty"`
	Err    string `json:"error,omitempty"`
	// Empty is how many listed runs hold no records.
	Empty int `json:"empty,omitempty"`
	// Trail: the notification trail was given (Options.Events). TrailUnread is how many runs' trails
	// could not be read.
	Trail       bool `json:"notify_trail"`
	TrailUnread int  `json:"trail_unread,omitempty"`
	// Filtered: the page shows a filter's runs only, so its headline cannot speak for the store.
	Filtered bool `json:"filtered,omitempty"`
}

Coverage says how much of the source a page read, and so what its headline may claim. A page is clear only when it read everything it should have: a headline that is silent about a run it never read would be a false all-clear. It is in the JSON of the run list and the Needs attention list.

func (Coverage) Gap added in v0.17.0

func (c Coverage) Gap() string

Gap is a line saying what the page did not read, or "" when it read everything it should have.

type Decision

type Decision struct {
	Approver   string `json:"approver,omitempty"` // empty for a one-decision gate's Approve
	Approved   bool   `json:"approved"`
	Signed     bool   `json:"signed"`
	DecidedAt  string `json:"decided_at,omitempty"`
	RecordedAt string `json:"recorded_at,omitempty"`
	// Note says why a recorded decision did not count, when the journal alone would make it look
	// as if it should have: agent.ReasonAfterExpiry for one written after the gate's expiry.
	Note string `json:"note,omitempty"`
	// contains filtered or unexported fields
}

Decision is one recorded approval decision. A signed decision carries two times: DecidedAt, the approver's clock inside the signature, and RecordedAt, the submitter's clock when it was journaled (not signed).

type Diff added in v0.15.0

type Diff struct {
	Old, New Config
}

Diff is a flow definition compared with an earlier version of it: bide check -diff. Each side is read as BuildConfig reads a config (plan.InspectConfig), so with a Registry on both sides the page also shows the digest each side's runs journal.

type DiffView added in v0.15.0

type DiffView struct {
	Name    string `json:"name"`
	OldName string `json:"old_name"`
	Flow    string `json:"flow"`
	// Typed reports both sides were checked against a plan.Registry, so their digests are known
	// (unless a side has errors).
	Typed bool `json:"typed"`
	// Basis is what the typed result rests on: SourceRegistry when both sides were checked against
	// a Registry, SourceDescription when a side was checked against a description file only (what
	// that side's blocks declare is then as the file says, not checked against the program), empty
	// when structural.
	Basis string `json:"basis,omitempty"`
	// OldBasis and NewBasis say how each side was checked: SourceRegistry, SourceDescription, or
	// "structural".
	OldBasis string `json:"old_basis"`
	NewBasis string `json:"new_basis"`
	// OldFile and NewFile are the SHA-256 of the description file of each side, when it has one.
	OldFile string `json:"old_file,omitempty"`
	NewFile string `json:"new_file,omitempty"`
	// Fallback is set when a side checked against a Registry or a description had errors, so the typed
	// comparison had nothing sound to read: the diff was made structurally, as it is without them,
	// which never reads a gate as stronger than it can tell. It says so in words and the diff is not
	// certified: -fail-on-weaken fails on it, and the verdict is never good.
	Fallback  string   `json:"fallback,omitempty"`
	OldDigest string   `json:"old_digest,omitempty"`
	NewDigest string   `json:"new_digest,omitempty"`
	Changes   []Change `json:"changes"`
	// Cannot, when set, says the sides could not be compared and why (a file with errors has no digest
	// to compare); nothing is then claimed about them, and Changes is empty.
	Cannot string `json:"cannot,omitempty"`
	// DigestNote is set when the digests differ and no change explains it: the digest covers more than
	// the diff itemises (a type spelled with another package path).
	DigestNote string `json:"digest_note,omitempty"`
	// Weakened and Strengthened name each change that made the flow's approval checks weaker or
	// stronger, with how: "charge: approval 2 of 3 -> 1 of 3" for a gate, "receipt: reachable
	// without an approval gate (validate -> receipt), not before; its safety is side effect" for a
	// node newly reachable around a gate, "assertion removed: every path to charge passes approval >= 2 of 3" for an assert line.
	Weakened     []string `json:"weakened,omitempty"`
	Strengthened []string `json:"strengthened,omitempty"`
	Counts       Counts   `json:"counts"`
	// OldErrors and NewErrors count each side's config errors (bide check lists them): a side
	// with errors is compared as far as it reads.
	OldErrors int    `json:"old_errors"`
	NewErrors int    `json:"new_errors"`
	Graph     *Graph `json:"graph,omitempty"`
	// Verdict is the answer the page opens with (see diffVerdict); it is not in the JSON view.
	Verdict Verdict `json:"-"`
	// contains filtered or unexported fields
}

DiffView is what the diff page shows.

func BuildDiff added in v0.15.0

func BuildDiff(d Diff) (*DiffView, error)

BuildDiff compares d.New with d.Old and draws the new flow with the old one's removed nodes and edges as ghosts. It fails only when a side cannot be read as a flow config at all.

func DiffTopologies added in v0.25.0

func DiffTopologies(d TopologyDiff) (*DiffView, error)

DiffTopologies compares a journaled run's topology (Old) with a file's (New) by what the digest covers. See TopologyDiff.

func (*DiffView) BasisLine added in v0.22.0

func (v *DiffView) BasisLine() string

BasisLine says how each side was checked when that is not plainly "both structural" or "both against the Registry": a description file for either side (its gates, safety and types are as the file says; the check against the program's Registry is the control), a different basis per side, or a diff that fell back to the structural comparison and is not certified. Empty otherwise.

func (*DiffView) DigestLine added in v0.15.0

func (v *DiffView) DigestLine() string

DigestLine says how the digest changed, in words, for the text summary and the page.

type Event

type Event struct {
	Seq    int      `json:"seq"`   // 1-based position in the journal
	Name   string   `json:"name"`  // the record's journal name
	Kind   string   `json:"kind"`  // agent.StepKind
	Class  string   `json:"class"` // the row's style: model, tool, attempt, approval, halt, end, value
	Title  string   `json:"title"`
	Detail string   `json:"detail,omitempty"`
	Badges []string `json:"badges,omitempty"`
	// RecordedAt is the record's own recorded_at, verbatim, on the records that carry one: the
	// claim of the process that wrote it, informational (see agent.Record.RecordedAt).
	RecordedAt string `json:"recorded_at,omitempty"`

	// What the page adds to the row; none of it is in the JSON view.
	At      time.Time `json:"-"` // when the journal says the record happened (a start, an attempt marker, or a record's recorded_at), else zero
	Payload string    `json:"-"` // the record as indented JSON, cut to a readable size (see prettyJSON)
	Rel     string    `json:"-"` // At as an offset from the run's start ("+3.2s")
	Abs     string    `json:"-"` // At in UTC, for the hover
	Bar     *TimeBar  `json:"-"` // the span from At to the next timestamped record
}

Event is one journal record, as a timeline row.

func (Event) IconName added in v0.16.0

func (e Event) IconName() string

IconName is the row's glyph.

type Exports

type Exports map[string][]agent.Record

Exports is a Source over exported journals (audit.JournalExport), by run ID.

func (Exports) History

func (x Exports) History(_ context.Context, runID string) ([]agent.Record, error)

History returns the exported run's records.

func (Exports) Runs

func (x Exports) Runs(context.Context) ([]string, error)

Runs lists the exported runs.

type Flow

type Flow struct {
	Topology plan.Topology
	// Digest is flow.Digest(), compared with the digest the run journaled. Empty: not compared.
	Digest string
	// Conform is flow.Conform. Nil: the viewer runs only its structural check (see Conformance).
	// Source says where the topology came from, as the page shows it.
	Conform func(ctx context.Context, j *agent.Journal, runID string) (bool, []string, error)
	Source  string
}

Flow is a declared flow the viewer draws a run against. The journal records a flow run's name and topology digest, not its topology, so the topology comes from the caller: a program that holds the *plan.Flow passes its Topology, Digest and Conform; the bide view command reads a Topology exported as JSON (json.Marshal(flow.Topology())) and, optionally, the digest.

type Follower added in v0.15.0

type Follower interface {
	// Grew reports whether runID has a record after the store sequence after (agent.Entry.Seq).
	// It is the read live mode polls, so it is cheap: one Load from after, stopped at its first entry.
	Grew(ctx context.Context, runID string, after int64) (bool, error)
	// HistorySeq is History with each record's store sequence, both read in one pass.
	HistorySeq(ctx context.Context, runID string) ([]agent.Record, []int64, error)
}

Follower is a Source whose runs can grow while the viewer reads them: a store, not an export. JournalSource is one.

type Gate

type Gate struct {
	ToolUseID  string     `json:"tool_use_id"`
	ToolName   string     `json:"tool_name,omitempty"`
	Single     bool       `json:"single,omitempty"` // a one-decision gate (agent.Approve)
	Need       int        `json:"need,omitempty"`
	Approvers  []string   `json:"approvers,omitempty"`
	ApprovedBy []string   `json:"approved_by,omitempty"`
	DeniedBy   []string   `json:"denied_by,omitempty"`
	Pending    []string   `json:"pending,omitempty"`
	Decisions  []Decision `json:"decisions,omitempty"`
	Tallied    bool       `json:"tallied"` // the gate journaled its terminal tally
	// AskedAt is when the run asked for approval, by the run's clock: a flow node's request time
	// (agent.ApprovalRequest.RecordedAt), else the pause's (agent.ApprovalWait.RecordedAt); empty
	// when the journal does not say.
	AskedAt string `json:"asked_at,omitempty"`
	// Waiting: the run paused at this gate (its approval-pending record, agent.ApprovalWait) and
	// the call has no result, no terminal tally, and (for a one-decision gate) no decision yet.
	Waiting bool `json:"waiting"`
	// Signatures is whether the gate's approver signatures were verified (audit.VerifyApprovals,
	// with -approver-keys), or why they were not checked; SignaturesOK reports a verified gate.
	Signatures   string `json:"signatures"`
	SignaturesOK bool   `json:"signatures_ok"`
	Status       string `json:"status"`
	// Reapproval: the gate's recorded tally passed on decisions that predate the signed decision
	// format (agent.DecisionFormat) and the call has not run, so the gate asks again when the run
	// resumes (agent.PendingReapproval); only decisions with a signed time count.
	Reapproval bool `json:"reapproval,omitempty"`
	// ReapprovalSoD: the re-ask is because the recorded tally passed without excluding the requester
	// and the gate's policy now excludes it (agent.PendingSoDReapproval); Reapproval is set too.
	ReapprovalSoD bool `json:"reapproval_sod,omitempty"`
	// Cards is each approver as the page shows them (see ApproverCard); not in the JSON view.
	Cards []ApproverCard `json:"-"`
	// Why says why the call needs this gate beyond its tool's own: for the escalation instance of a tainted
	// call, the untrusted content its turn had read; for a gate a cap rule holds, the rule and this call's
	// figures as of the ask. It is the engine's gate-reason record, the application's claim, and no gate reads it.
	Why []gatereason.Reason `json:"why,omitempty"`
	// ExcludePrincipal: the gate never counts the run's requester (agent.ApprovalPolicy.ExcludePrincipal);
	// Requesters are the requesters (the run's principal and those of the runs above it), and Excluded
	// the approvers who fill no seat (the tally's, or before
	// it, the approver ids equal to the requester ignoring case).
	ExcludePrincipal bool     `json:"exclude_principal,omitempty"`
	Requesters       []string `json:"requesters,omitempty"`
	Excluded         []string `json:"excluded,omitempty"`
	// ExpiresAt is the gate's recorded deadline (agent.ApprovalDeadline), in the journal's timestamp
	// form, and Timeout the timeout it was opened with ("24h"); both empty for a gate with none.
	ExpiresAt string `json:"expires_at,omitempty"`
	Timeout   string `json:"timeout,omitempty"`
	// Expired: the gate's recorded outcome is its expiry (agent.GateStanding.Expired): the call never
	// ran, and a decision is not counted.
	Expired bool `json:"expired,omitempty"`
	// Overdue: the gate still waits and the viewer's clock is past ExpiresAt. Only a drive of the run
	// records the expiry; until then the gate refuses decisions (agent.ErrApprovalExpired) and the
	// run is not finished.
	Overdue bool `json:"overdue,omitempty"`
	// Due says where the deadline stands on the viewer's clock ("due in 40m 00s (... UTC)"); empty
	// for a gate with no deadline.
	Due string `json:"due,omitempty"`
	// Identity is the gate's approver identity, for a gate with a certificate-bound approver; nil for
	// a gate of bare keys (see GateIdentity).
	Identity *GateIdentity `json:"identity,omitempty"`
	// Held: the call is counted but not run, held before execution by the identity status re-check
	// (agent.ExecHold); the run needs a person although the gate is no longer waiting for decisions.
	Held bool `json:"held,omitempty"`
}

Gate is a tool call's or a plan flow node's approval gate, from the decisions and tally the journal holds. A flow node's ToolUseID is its key ("node:<name>", "node:iter:<n>:<name>").

func (Gate) Class added in v0.14.0

func (gt Gate) Class() string

Class is the gate's style: good, wait or bad.

func (Gate) DecisionAt added in v0.17.0

func (g Gate) DecisionAt() string

DecisionAt is when a one-decision gate's decision was journaled, as the page shows it ("2026-10-13 12:14:03.123"): the claim of the process that called agent.Approve, not signed. It is empty for an m-of-n gate (its approver cards show the times), for a gate with no decision, and for a decision that records none. A time that is not in the canonical form is not shown.

func (Gate) HasTimes added in v0.16.0

func (g Gate) HasTimes() bool

HasTimes reports whether any of the gate's approver cards shows a decision time.

func (Gate) Headline added in v0.14.0

func (gt Gate) Headline() string

Headline is the gate's state in a few words, as the page's gate card and the node's box show it.

func (Gate) SigLine added in v0.14.0

func (gt Gate) SigLine() string

SigLine is the gate's signature verdict in a few words.

func (Gate) WhyLines added in v0.24.0

func (g Gate) WhyLines() []string

WhyLines is the gate's reasons as lines of text (gatereason.Reason.Line), for the page.

type GateIdentity added in v0.19.0

type GateIdentity struct {
	Bound bool `json:"bound"`
	// Approvers are the approver ids that are certificate-bound, in policy order; the other approvers
	// of the gate are bare keys, whose decisions carry no identity.
	Approvers    []string `json:"approvers,omitempty"`
	PolicyDigest string   `json:"policy_digest,omitempty"`
	// Phase is the pause note's Pause (agent.Pause*), PhaseText the words for it ("paused: identity
	// status unknown"), empty when the gate is simply waiting for decisions, and Detail the note's
	// sentence.
	Phase     string `json:"phase,omitempty"`
	PhaseText string `json:"phase_text,omitempty"`
	Detail    string `json:"detail,omitempty"`
	// Unchecked are certificate-bound approvers who decided on a gate that has not counted yet: the
	// decision is recorded, and nothing here says it counts.
	Unchecked []string       `json:"unchecked,omitempty"`
	Cards     []IdentityCard `json:"cards,omitempty"`
	Hold      *IdentityHold  `json:"hold,omitempty"`
	// Paused: the newest phase note says exec-hold but the journal holds no hold record, which is the
	// gate waiting for current status before it runs the call (nothing authoritative is written; the same
	// approvals run once status is fresh), not a revoked or no-longer-verifying decision.
	Paused bool `json:"paused,omitempty"`

	// Generation is the gate's newest count (0 is the first); Reasked says a call was held before
	// execution and the gate asked again, so earlier counts are history.
	Generation int  `json:"generation,omitempty"`
	Reasked    bool `json:"reasked,omitempty"`
	// Problems are why the identity data cannot be relied on, in words.
	Problems []string `json:"problems,omitempty"`
	// Class is audited, final, prov, bad or wait (see the type's comment); Line is one line saying it.
	Class string `json:"class"`
	Line  string `json:"line"`
	// contains filtered or unexported fields
}

GateIdentity is what the page and bide approve -list show of a gate with a certificate-bound approver (docs/design/approver-identity.md): which approvers are bound to a certificate, the policy digest the gate paused under, what the gate paused for, each counted decision's identity (IdentityCard), and what does not check out. It is read from the journal alone (the pause note, the :ident tally, the approval-identity record and the decision records); this page does not recompute the policy digest, verify a certificate chain or fetch status. That is the gate's job when it counts and the auditor's when it re-checks.

Every value is the gate's claim at counting ("as recorded"), never a verification. The styles it derives: "audited" (the only green) needs a result from an audit the viewer was given (ApprovalCheck.Identity) that re-checked every approving decision and found none disputed; "final" is final as recorded, in a neutral style; provisional, waiting, held, incomplete, stripped, mismatched and disputed each have their own visibly non-final style. Nothing is called "verified" without an audit result.

type Graph

type Graph struct {
	Nodes   []GraphNode `json:"nodes"`
	Edges   []GraphEdge `json:"edges"`
	SVG     string      `json:"-"`
	Mermaid string      `json:"mermaid"`
	Width   int         `json:"-"`
	Height  int         `json:"-"`
	// contains filtered or unexported fields
}

Graph is the declared topology with the run's path overlaid.

type GraphEdge

type GraphEdge struct {
	From    string `json:"from"`
	To      string `json:"to"`
	Label   string `json:"label,omitempty"`
	Taken   bool   `json:"taken"`
	Back    bool   `json:"back,omitempty"`
	Problem string `json:"problem,omitempty"` // a config problem at this edge or arm
	Cex     bool   `json:"cex,omitempty"`     // on a violated assertion's counterexample path
	// Change is how a flow diff changed this edge or arm (added, removed or changed), which
	// styles it; empty outside a diff.
	Change string `json:"change,omitempty"`
	// contains filtered or unexported fields
}

GraphEdge is one declared edge or Switch arm, and whether the run took it.

type GraphNode

type GraphNode struct {
	Name   string `json:"name"`
	Kind   string `json:"kind"`
	Status string `json:"status"` // done, halted, attempted, waiting (at its approval gate), not-reached
	// Gate is the node's declared approval gate ("2 of 3", "one decision"), empty for none, and
	// GateStatus where its latest evaluation stands (see Gate.Status).
	Gate       string `json:"gate,omitempty"`
	GateStatus string `json:"gate_status,omitempty"`
	// GateLine and SigLine are what the node's box says about its gate, short enough to read in
	// a screenshot: "approved 2 of 3" or "waiting, 1 of 2 approvals", and "signatures verified" (or
	// not, and why in short). GateClass styles them: good, wait or bad.
	GateLine  string `json:"gate_line,omitempty"`
	SigLine   string `json:"sig_line,omitempty"`
	GateClass string `json:"gate_class,omitempty"`
	Iters     int    `json:"iters,omitempty"`
	Last      bool   `json:"last,omitempty"`
	Note      string `json:"note,omitempty"`  // a line under the box
	Title     string `json:"title,omitempty"` // the tooltip, when not "name: status"
	// Risk is a config node's safety class ("side effect", "read-only", "idempotent", "safety
	// unknown") and Crash its crash behavior in a few words; empty on a run's graph. Cex marks a
	// node on a violated assertion's counterexample path.
	Risk  string `json:"risk,omitempty"`
	Crash string `json:"crash,omitempty"`
	Cex   bool   `json:"cex,omitempty"`
	// contains filtered or unexported fields
}

GraphNode is one declared node and what the run did with it.

type Halt

type Halt struct {
	Op          string    `json:"op"` // "tool" or "step"
	ID          string    `json:"id"` // the tool-use ID, or the step name ("node:<name>" for a plan node)
	ToolName    string    `json:"tool_name,omitempty"`
	Marker      string    `json:"marker"`
	AttemptedAt time.Time `json:"attempted_at,omitzero"`
	Ref         string    `json:"ref"` // the agent.HaltRef to resolve it with, as Go
}

Halt is an operation whose attempt marker is live with no result: a resume of the run returns *agent.OutcomeUnknown for it until agent.ResolveHalt records its outcome.

type Head struct {
	Title string
	Icon  template.URL
	// Refresh, when positive, makes the page reload itself after that many seconds (a live page
	// that needs no script).
	Refresh int
}

Head is what a page's <head> carries that differs between pages: its title and its icon.

type HeldCounts added in v0.21.0

type HeldCounts struct {
	// Held runs are held by the stop for certain; May runs may be (an agent stop: the journal does not record
	// the agent identity that drives a run). Read is how many unfinished runs were read, Unread how many could
	// not be, and Truncated says more than maxHeldRead were listed and only the first were read.
	Held, May, Read, Unread int
	Truncated               bool
	// ListErr is set when the runs could not be listed, so nothing was counted. Incomplete is set when the count
	// was cut off (its context ended) after counting some: the counts are then lower bounds.
	ListErr    string
	Incomplete bool
}

HeldCounts is how many of a store's unfinished runs one stop holds (see HeldCount).

func HeldCount added in v0.21.0

func HeldCount(ctx context.Context, src JournalSource, e agent.StopEntry) HeldCounts

HeldCount reads the unfinished runs of src and counts those the stop e holds, or may hold: what a person lifting a stop is about to let carry on. It is a read of every unfinished run, bounded by maxHeldRead.

type IdentityAudit added in v0.19.0

type IdentityAudit struct {
	OK           bool
	Disputed     bool
	Detail       string
	TallyDigest  string
	PolicyDigest string
	CheckedAt    string
}

IdentityAudit is an audit's finding on one certificate-bound decision: OK that the audit recomputed the tier from the decision record, the heads, the chain and the status bytes and agrees with what the gate recorded, Disputed that newer status shows a revocation effective at or before the decision became provable (keyCompromise voids the count back to the certificate's notBefore), with Detail.

TallyDigest and PolicyDigest name the count the audit examined (the SHA-256 of the recorded tally's bytes and of the identity record's policy); a result whose digests are not the journal's current ones is not applied. CheckedAt is when the audit ran (an RFC 3339 time).

type IdentityCard added in v0.19.0

type IdentityCard struct {
	Approver   string `json:"approver"`
	Identifier string `json:"identifier"`
	// DisplayName is the certificate's subject common name, read from the decision record when its
	// leaf is the one the identity record names. It is the CA's label, not part of the identity.
	DisplayName string `json:"display_name,omitempty"`
	Tier        string `json:"tier"`
	// TierText names the tier in words; Class is audited, final, prov or bad.
	TierText  string `json:"tier_text"`
	Class     string `json:"class"`
	BasisKind string `json:"basis_kind,omitempty"`
	Basis     string `json:"basis,omitempty"`
	// Audited: an audit the viewer was given re-checked this decision and agrees (green only then);
	// StatusAge is S's age on the viewer's clock (MarkDeadlines).
	Audited   bool   `json:"audited,omitempty"`
	StatusAge string `json:"status_age,omitempty"`

	// StatusTime is S, the time of the revocation status the count rests on; AnchoredAt is when the
	// decision became provable and TimeSource what that time rests on; Window is W'. All are display
	// text in UTC.
	StatusTime string   `json:"status_time,omitempty"`
	AnchoredAt string   `json:"anchored_at,omitempty"`
	TimeSource string   `json:"time_source,omitempty"`
	Window     string   `json:"window,omitempty"`
	Leaf       string   `json:"leaf,omitempty"` // the first 16 hex digits of the leaf certificate's hash
	Warnings   []string `json:"warnings,omitempty"`
	// Disputed is set only from an audit result (ApprovalCheck.Identity), never from the journal: an audit found newer status showing a revocation (keyCompromise) effective at or
	// before the decision became provable, which voids the count back to the certificate's notBefore.
	Disputed bool `json:"disputed,omitempty"`
	// contains filtered or unexported fields
}

IdentityCard is one counted decision's identity as the identity record keeps it.

type IdentityHold added in v0.19.0

type IdentityHold struct {
	Reason     string   `json:"reason"`
	Detail     string   `json:"detail,omitempty"`
	Approvers  []string `json:"approvers,omitempty"`
	CheckedAt  string   `json:"checked_at,omitempty"`
	StatusTime string   `json:"status_time,omitempty"`
	Serial     string   `json:"serial,omitempty"`
}

IdentityHold is the pre-execution status re-check that held a call (agent.ExecHold).

type JournalSource

type JournalSource struct{ J *agent.Journal }

JournalSource reads a store through a Journal. Runs lists through the store's agent.Lister, when it has one.

func (JournalSource) CanList added in v0.17.0

func (s JournalSource) CanList() bool

CanList reports whether the store can enumerate its runs (it has an agent.Lister). A page over one that cannot knows nothing about its runs, and says so.

func (JournalSource) Grew added in v0.15.0

func (s JournalSource) Grew(ctx context.Context, runID string, after int64) (bool, error)

Grew implements Follower.

func (JournalSource) History

func (s JournalSource) History(ctx context.Context, runID string) ([]agent.Record, error)

History reads the run's records.

func (JournalSource) HistorySeq added in v0.15.0

func (s JournalSource) HistorySeq(ctx context.Context, runID string) ([]agent.Record, []int64, error)

HistorySeq implements Follower. It reads the run through a Journal over the same store, so each record is decoded and checked as History does, and keeps the Seq of each entry the store yields.

func (JournalSource) Runs

func (s JournalSource) Runs(ctx context.Context) ([]string, error)

Runs lists the store's runs, or none when the store cannot list them.

func (JournalSource) Unfinished added in v0.17.0

func (s JournalSource) Unfinished(ctx context.Context) ([]string, error)

Unfinished lists the runs that have no run:complete marker, in the store's own query: a run that completed has nothing a person must do, so the Needs attention list reads none. An aborted or cancelled run is listed (a halt outlives either).

func (JournalSource) UnfinishedLimit added in v0.21.0

func (s JournalSource) UnfinishedLimit(ctx context.Context, limit int) ([]string, error)

UnfinishedLimit is Unfinished that stops listing after limit runs (no limit when limit is 0), so a store with a very large number of unfinished runs is not loaded whole to read the first few.

type KindOption added in v0.15.0

type KindOption struct {
	Value, Label string
	On           bool
}

KindOption is one of the kind filter's options.

type ListFilter added in v0.15.0

type ListFilter struct {
	State string `json:"state,omitempty"`
	Kind  string `json:"kind,omitempty"`
	Q     string `json:"q,omitempty"`
	Limit int    `json:"limit"`
}

ListFilter is what the run list shows: the runs in a state group and of a kind, whose ID contains Q (ignoring case), at most Limit of them. Empty fields admit every run.

type Listable added in v0.17.0

type Listable interface{ CanList() bool }

Listable is a Source that can say whether it can enumerate its runs. A Source that does not implement it is taken to.

type LiveUpdate added in v0.15.0

type LiveUpdate struct {
	Changed bool `json:"changed"`
	// Live: the run has no end marker yet, so the page polls again; false once it has one.
	Live bool `json:"live"`
	// Cursor is the store sequence of the run's last record: the after of the next poll.
	Cursor    int64   `json:"cursor"`
	State     string  `json:"state,omitempty"`
	StateText string  `json:"state_text,omitempty"`
	Events    []Event `json:"events,omitempty"` // the rows of the records after the cursor given, only
}

LiveUpdate is GET /run/live's answer: whether the run has records after the cursor the page gave, and, when it has, the timeline rows of those records and the run's state now.

type MerklePath added in v0.16.0

type MerklePath struct {
	Seq   int    // the record's 1-based position in the journal
	Name  string // its journal name
	Size  int    // the journal length the signed root commits to
	Leaf  string // the record's leaf hash, hex
	Steps []MerkleStep
	Root  string // the signed root, hex
	// Climbed reports that hashing the path from the leaf gave Root, which the page checks again
	// here for the intermediate hashes it shows; the proof itself was verified by audit.
	Climbed  bool
	Alg      string
	SignedAt time.Time
}

MerklePath is one record's climb to the signed root: its leaf hash, the sibling hash at each level of the RFC 6962 audit path and the node it combines into, ending at the root the signed tree head commits to. The page draws it as a small diagram. It is built from the proof the audit package verified (audit.ProofBundle), so the diagram shows a path that holds.

func (*MerklePath) Caption added in v0.16.0

func (m *MerklePath) Caption() string

Caption says what the diagram shows.

func (*MerklePath) Diagram added in v0.16.0

func (m *MerklePath) Diagram() template.HTML

Diagram is the path drawn as an SVG, or empty when it did not climb to the root (the page then shows no diagram rather than one that does not hold).

type MerkleStep added in v0.16.0

type MerkleStep struct {
	Sibling string
	Left    bool // the sibling is the left child
	Node    string
}

MerkleStep is one level: the sibling hash, which side it is on, and the node they make.

type NotifyStatus added in v0.17.0

type NotifyStatus struct {
	State string `json:"state"`
	Text  string `json:"text"`
	// Deadline is when the notification is overdue if unacknowledged; Overdue says it is.
	Deadline time.Time `json:"deadline,omitzero"`
	Overdue  bool      `json:"overdue,omitempty"`
}

NotifyStatus is what the notify trail says about an item.

func (NotifyStatus) Class added in v0.17.0

func (n NotifyStatus) Class() string

Class styles the status: good once acknowledged; bad when delivery fails, an event is overdue, nothing was recorded, or the notifier cleared an item the journal still shows open; else warn.

type OpenedStore added in v0.25.0

type OpenedStore struct {
	Source  Source
	Journal *agent.Journal
	Store   agent.Store
	Closer  io.Closer
}

OpenedStore is what StoreFlags.Open yields. Journal and Store are nil for exported journals, which have no store behind them. Closer is nil when there is nothing to close.

type Options

type Options struct {
	Source Source
	// Flows are the declared flows runs are drawn against, by flow name.
	Flows map[string]*Flow
	// Provers check runs against audit artifacts; the first to return a verdict for a run wins.
	Provers []Prover
	// Config, when set, makes the handler show this flow definition instead of runs: the page at /
	// and its view as JSON at /config.json. Source is then not read.
	Config *Config
	// Diff, when set, makes the handler show a flow definition compared with an earlier one
	// instead of runs: the page at / and its view as JSON at /diff.json (see BuildDiff).
	Diff *Diff
	// Events, when set, is the notification trail's store (the audit.EventStore beside the
	// journal): the Needs attention list then says for each item whether it was notified and
	// acknowledged, and lists a run whose recovery keeps failing.
	Events audit.EventStore
	// ApproveCommand, when set, writes the bide approve command line that records a decision on a
	// call of a run (single: a one-decision gate). The Needs attention list shows it with each
	// pending approval; without it the list says it has none.
	ApproveCommand func(runID, toolUseID string, single bool) (string, error)
	// ApproveCertCommand, when set, writes the bide approve -cert command line for a gate with a
	// certificate-bound approver: a bare-key decision does not count for that approver. The Needs
	// attention list shows it beside (a gate with bare-key approvers too) or instead of (all approvers
	// certificate-bound) ApproveCommand's line.
	ApproveCertCommand func(runID, toolUseID string) (string, error)
	// Stops, when set, is the kill switch's control run of the journal: every page then opens with a banner
	// while a stop is in force (or the control run cannot be judged, or holds rejected records), a run's page
	// and the run list say which runs a stop holds, and /stops.json serves the same state.
	Stops *StopFeed
	// Themes are custom themes (see the theme package) the pages offer beside the shipped light, dark
	// and high-contrast; DefaultTheme, when not empty, names the one a reader starts in. Handler
	// panics on a DefaultTheme that names no theme and on two themes with one name: they are a host
	// program's mistake, and bide view checks both before it calls Handler.
	Themes       []*theme.Theme
	DefaultTheme string
	// ThemeLock, when not empty, names the one theme the pages have: no switch, no remembered
	// choice (the system's request for more contrast or forced colors still wins). Handler panics
	// on a ThemeLock that names no theme or that differs from a DefaultTheme.
	ThemeLock string
	// Now is the clock the Needs attention list measures waits by; nil is time.Now.
	Now func() time.Time
	// contains filtered or unexported fields
}

Options configure Handler.

type Proof

type Proof struct {
	Source   string   `json:"source"` // "signed tree head" or "evidence package"
	OK       bool     `json:"ok"`
	Summary  string   `json:"summary"`
	Covers   int      `json:"covers"` // journal records the signed head commits to
	Problems []string `json:"problems,omitempty"`
	Items    []string `json:"items,omitempty"`
	// Approvals is the signature verdict on each m-of-n gate that journaled its tally, by
	// tool-use ID: audit.VerifyApprovals over the gate's evidence, with the approver keys given.
	// Nil without approver keys.
	Approvals map[string]ApprovalCheck `json:"approvals,omitempty"`

	// What the page draws from the proof; none of it is in the JSON view.
	Merkle   *MerklePath `json:"-"` // the proven record's path to the signed root
	SignedAt time.Time   `json:"-"` // when the signed head was signed
}

Proof is the audit verdict on the run's journal, from the audit package's own verifiers (the code bide audit verify and verify-evidence run), never a reimplementation.

type Prover

type Prover func(ctx context.Context, runID string, recs []agent.Record) *Proof

Prover checks a run's records against an audit artifact. It returns nil for a run the artifact is not about. When no prover returns a proof for a run, the signatures line of its gates says so; the provers HeadProver and EvidenceProver also name, there, the run their artifact is about. A prover of the caller's that returns nil adds no name and gets the plain line.

func EvidenceProver

func EvidenceProver(pkg audit.EvidencePackage, v audit.Verifier, ap Approvals, opts ...audit.EvidenceVerifyOption) Prover

EvidenceProver checks an evidence package (audit.EvidencePackage.Verify, as bide audit verify-evidence does) and that its signed head commits to the journal shown.

With approver keys (ap.Keys non-nil), it also checks each m-of-n gate's signatures with audit.VerifyApprovals over the package's actions, as bide audit verify-approvals does.

func HeadProver

func HeadProver(sth audit.SignedTreeHead, v audit.Verifier, ap Approvals) Prover

HeadProver checks a signed tree head: its signature under v and the timestamp rule (audit.CheckTimestamp), and that its root commits to the first sth.Size records of the journal shown, by building and verifying an inclusion proof of the last of them (audit.ProveRecord, ProofBundle.Verify), as bide audit prove and verify do.

With approver keys (ap.Keys non-nil), it also checks each m-of-n gate's signatures: it builds the gate's evidence from the journal under sth (audit.ApprovalEvidence) and verifies it with audit.VerifyApprovals, as bide audit verify-approvals does.

type RiskSummary added in v0.15.0

type RiskSummary struct {
	SideEffects int `json:"side_effects"`
	Gated       int `json:"gated"`
	Ungated     int `json:"ungated"`
	ReadOnly    int `json:"read_only"`
	Idempotent  int `json:"idempotent"`
	Unknown     int `json:"unknown"`
	// Line is the headline, as "3 side-effect steps, 2 gated, 1 ungated" ("steps known" when a
	// class is unknown).
	Line string `json:"line"`
	// UnknownLine says why some classes are unknown, empty when none is.
	UnknownLine string `json:"unknown_line,omitempty"`
}

RiskSummary counts a config's nodes by safety class: side effects (gated, when every path to one passes an approval gate, and ungated), read-only, idempotent, and unknown (the structural check cannot see a block's registered Safety).

type RunList added in v0.15.0

type RunList struct {
	Filter ListFilter `json:"filter"`
	Rows   []RunRow   `json:"runs"`
	// Matched is how many runs the filter admits; Rows holds the first Filter.Limit of them.
	Matched int `json:"matched"`
	// Summarized is how many runs were read; Listed how many the source lists. They differ when
	// more than maxSummarized runs match the ID filter.
	Summarized int      `json:"summarized"`
	Listed     int      `json:"listed"`
	Kinds      []string `json:"kinds"` // the kinds of the runs read, for the kind filter
	// Counts are the runs of each state group the ID and kind filters admit, and All their sum:
	// the state filter's counts.
	Counts map[string]int `json:"counts"`
	All    int            `json:"all"`
	// Recovering is how many of the runs counted have an open recovery failure episode in the
	// notification trail and have not ended.
	Recovering int      `json:"recovering,omitempty"`
	More       string   `json:"more,omitempty"` // the page with listPage more rows, when there are more
	Notes      []string `json:"notes,omitempty"`
	// Status is what the headline says, for a script: attention, clear or unknown. Coverage says what
	// was read; the headline is ALL CLEAR only when it is complete (see Coverage.nothingFound).
	Status string `json:"status"`
	Coverage
}

RunList is the run list page: the runs the filter admits, newest first by start time, then the runs that record none by run ID.

func (RunList) KindOptions added in v0.15.0

func (l RunList) KindOptions() []KindOption

KindOptions are the kind filter's options: the kinds of the runs read, and the chosen one.

func (RunList) States added in v0.15.0

func (l RunList) States() []StateOption

States are the state filter's options: every run, then each state group that has a run or is chosen.

func (RunList) Verdict added in v0.16.0

func (l RunList) Verdict() Verdict

Verdict is the run list's headline: what needs a person first. It is ALL CLEAR only when the journals and the notify trail were read in full and no run has a failing recovery (a recovery failure is not in the journals, and a run never read cannot be called clear); otherwise it says what it cannot rule out (see Coverage.nothingFound).

type RunRow added in v0.15.0

type RunRow struct {
	ID        string `json:"run_id"`
	Kind      string `json:"kind"`  // agent.RunKind, or "" when the run has no run:start
	State     string `json:"state"` // the View state (see Build)
	StateText string `json:"state_text"`
	Group     string `json:"group"` // the state filter group
	Flow      string `json:"flow,omitempty"`
	Records   int    `json:"records"`
	// Held are the active kill switch stops that hold, or may hold, the run (see heldBy).
	Held []StopHold `json:"held_by,omitempty"`
	// Started is when the run's first drive started, as its run:start records it
	// (agent.RunStart.Started). Zero for a run journaled by a version that recorded none.
	Started time.Time `json:"started,omitzero"`
}

RunRow is one run as the run list shows it.

func (RunRow) KindText added in v0.15.0

func (r RunRow) KindText() string

KindText is the run's kind as the list shows it.

func (r RunRow) Link() string

Link is the run's page.

func (RunRow) StartedText added in v0.15.0

func (r RunRow) StartedText() string

StartedText is Started as the list shows it, or "" when the run records no start time.

type Source

type Source interface {
	Runs(ctx context.Context) ([]string, error)
	History(ctx context.Context, runID string) ([]agent.Record, error)
}

Source is what the viewer reads: the runs it lists and each run's records. Both are reads.

type Stat added in v0.16.0

type Stat struct {
	Value, Label string
	Sub          string // one line under the label
	Link         string // where the number links to, if anywhere
	Class        string // good, bad, warn or empty
}

Stat is one key number under the verdict.

type StateOption added in v0.15.0

type StateOption struct {
	Value, Label string
	Count        int
	Link         string // the list with this option chosen, the other filters kept
	On           bool   // the option is the filter's
}

StateOption is one of the state filter's options.

type StopBanner added in v0.21.0

type StopBanner struct {
	// Mode is the control's mode, basic or audited. Verified says whether signatures were checked.
	Mode     string `json:"mode"`
	Verified bool   `json:"signatures_verified"`
	// Active are the stops in force (the first maxBannerStops), MoreActive how many more there are.
	Active     []StopLine `json:"active"`
	MoreActive int        `json:"more_active,omitempty"`
	// Unknowable says why the stops cannot be judged (a basic viewer on a signed deployment, a policy chain it
	// cannot walk): the engine holds side-effect calls a stop could cover while that is so.
	Unknowable string `json:"unknowable,omitempty"`
	// Unverified counts signed records a viewer with no stop policy cannot check.
	Unverified int `json:"unverified,omitempty"`
	// Rejected are records in the control run that have no effect (a forged record, a signer the policy does
	// not enrol); RejectedTotal counts them all.
	Rejected      []string `json:"rejected,omitempty"`
	RejectedTotal int      `json:"rejected_total,omitempty"`
	// ReadErr says the control run could not be read, by class: the banner never claims "no stop" then.
	ReadErr string `json:"read_error,omitempty"`
	// PlainIgnored counts plain (unsigned) stops in force for basic processes that this audited view ignores:
	// a deployment that has not moved to audited mode, or a process still configured basic, is stopped while
	// the audited processes are not.
	PlainIgnored int `json:"plain_ignored,omitempty"`
	// ReadAt is when the control run was read (the banner is served again for a couple of seconds).
	ReadAt string `json:"read_at,omitempty"`
}

StopBanner is the kill switch's state as every page of the viewer shows it, and /stops.json serves it.

func (StopBanner) Show added in v0.21.0

func (b StopBanner) Show() bool

Show reports whether the banner has anything to say. A control run with no stop, no rejected record and nothing unreadable shows no banner.

func (StopBanner) Stopped added in v0.21.0

func (b StopBanner) Stopped() bool

Stopped reports whether a stop is in force.

type StopFeed added in v0.21.0

type StopFeed struct {
	Control *agent.StopControl
	// Plain, set with an audited Control, is a basic control over the same journal: the stops it sees that
	// are not signed are stops a basic process enforces and an audited one ignores, which the banner says
	// (a deployment that has not upgraded, read by a viewer given the stop policy).
	Plain *agent.StopControl
	// Verified is true when the control checks signatures: the viewer was given the deployment's stop policy.
	Verified bool
}

StopFeed is how the viewer reads the kill switch: the control run of the journal it shows, folded by an agent.StopControl in the mode the viewer was given (a stop policy makes it audited and its signatures checked; without one it reads as a basic control, and says that signatures are not verified). Reading is all it does: the viewer never sets or lifts a stop.

type StopHold added in v0.21.0

type StopHold struct {
	Stop  string `json:"stop"` // the stop's ID
	Scope string `json:"scope"`
	// May is true when the journal cannot say: an agent stop covers the runs of an agent identity, which the
	// journal does not record, so any unfinished run may be one of them.
	May bool `json:"may,omitempty"`
}

StopHold says a stop holds, or may hold, a run.

func (StopHold) Text added in v0.21.0

func (h StopHold) Text() string

Text is the hold as the page says it.

type StopLine added in v0.21.0

type StopLine struct {
	ID           string `json:"id"`
	Scope        string `json:"scope"`
	Reason       string `json:"reason"`
	By           string `json:"by"`
	Host         string `json:"host,omitempty"`
	At           string `json:"at"`
	Age          string `json:"age"`
	Signed       bool   `json:"signed"`
	Compensation string `json:"compensation"`
	LiftsHeld    int    `json:"lifts_held"`
	LiftsNeeded  int    `json:"lifts_needed"`
}

StopLine is one active stop as the banner shows it.

type StoreFlags added in v0.25.0

type StoreFlags struct {
	DB          string
	Postgres    string
	PostgresEnv string
	Journals    []string
}

StoreFlags are the flags that name the journal a command reads: -db, -postgres, -postgres-env and -journal. At most one kind is given.

func (StoreFlags) Kinds added in v0.25.0

func (f StoreFlags) Kinds() int

Kinds is how many kinds of store the flags name: a SQLite file, a Postgres DSN (either flag), an exported journal.

func (StoreFlags) Open added in v0.25.0

func (f StoreFlags) Open(stores Stores) (OpenedStore, error)

Open opens what the flags name, read-only, through stores. It opens nothing when no flag is given (the result is zero). An exported journal is read whole.

func (*StoreFlags) Register added in v0.25.0

func (f *StoreFlags) Register(fs *flag.FlagSet)

Register adds the four flags to fs.

func (StoreFlags) Validate added in v0.25.0

func (f StoreFlags) Validate() string

Validate returns the usage message for flags that do not go together, or "". Naming no store is valid here: a command that needs one checks Kinds. The message never echoes a flag's value, which may be a DSN with a password.

type Stores

type Stores struct {
	SQLite   func(path string) (agent.Store, io.Closer, error)
	Postgres func(dsn string) (agent.Store, io.Closer, error)
	// Events returns the notification trail's store beside a store the opener returned, or nil when
	// the store keeps none. A nil Events means the Needs attention list says nothing about
	// notification.
	Events func(agent.Store) audit.EventStore
	// YAML converts a YAML flow config to the JSON plan reads; nil means this build reads JSON
	// configs only.
	YAML func(data []byte) ([]byte, error)
}

Stores opens the stores bide view reads, read-only. It is how the command plugs in store backends (store/sqlite's and store/postgres's OpenReadOnly) without the core module importing one. A nil opener means this build cannot read that kind of store.

type ThemeSet added in v0.22.0

type ThemeSet struct {
	Extra   []*theme.Theme
	Default string
	Lock    string
}

ThemeSet is the themes a page offers: Extra, custom themes beside the shipped light, dark and high-contrast; Default, the name of the theme a reader starts in (empty: the system's setting picks light or dark); and Lock, the name of the one theme the page has (no switch, no remembered choice). The system's request for more contrast or forced colors wins over a Default and a Lock.

func ThemeFlags added in v0.22.0

func ThemeFlags(file, def string, stderr io.Writer, errPrefix string) (set ThemeSet, usageMsg string, status int)

ThemeFlags reads the -theme file and checks -theme-default for bide view and bide edit, after the other flags have been validated (a flag error is a usage error whatever the theme file holds) and before anything is opened or served. It returns the set of themes to serve; or a status of 1 when the file is refused (the failed checks are written to stderr after errPrefix); or a usage message when the default names no theme, which the caller reports as a usage error (status 2).

func (ThemeSet) Validate added in v0.22.0

func (s ThemeSet) Validate() error

Validate reports why the set cannot be served: a theme that is nil or not from theme.Parse, Load, Shipped or Lookup, two themes with one name (a custom theme named like a shipped one included), a Default or Lock that names no theme, or a Default and a Lock that differ.

type TimeBar added in v0.16.0

type TimeBar struct {
	X, W  float64
	Label string
}

TimeBar is a timeline row's span on the run's clock, in percent of the whole: from the row's own time to the next timestamped record's. The journal timestamps the run's start, its attempt markers, and the records that note when they were written (a single approver's decision, a resolved halt, an end marker), so for an attempt this is how long it had been open when the next timestamped record was written, an upper bound on the operation, not its duration.

type TooLargeError added in v0.25.0

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

TooLargeError is the error DiffTopologies returns for a side past MaxTopologyNodes or MaxTopologyEdges: the sides were not compared, which a caller shows as "too large to compare" and never as "no difference".

func (*TooLargeError) Error added in v0.25.0

func (e *TooLargeError) Error() string

Error implements error.

type TopologyDiff added in v0.25.0

type TopologyDiff struct {
	// Old and New are the sides; Old is the run's.
	Old, New plan.ConfigReport
	// OldName and NewName label them.
	OldName, NewName string
	// Typed says both sides know their kinds, types and gates: the file was checked against a
	// plan.Registry (or a description of one). Without it the comparison is structural: flow name,
	// entry, node names and blocks, edges, Switch arms and their order. A journaled gate is the
	// resolved policy (a block's own Go gate, a one-decision gate), which a structural file cannot
	// show, so gates, and every verdict about them, are compared only when Typed.
	Typed bool
	// Basis is what the file's typed result rests on (SourceRegistry or SourceDescription), empty
	// when structural.
	Basis string
}

TopologyDiff is two sides compared as what a run's resume compares: the topology digest.

A journaled run holds a plan.Topology and nothing else, so each side is a report (plan.InspectTopology for the run, plan.InspectConfig for the file). The comparison counts only what the digest covers and nothing it does not: safety and assert lines are in neither a journal nor the digest, so they are never compared, and the order of nodes, edges and arms, which the digest commits to and the config diff does not itemise, is.

type UnfinishedLister added in v0.17.0

type UnfinishedLister interface {
	Unfinished(ctx context.Context) ([]string, error)
}

UnfinishedLister is a Source that can list the runs without a run:complete marker alone, so the Needs attention list reads no finished run.

type Verdict added in v0.16.0

type Verdict struct {
	// Class styles the banner: good, bad, warn, wait or info.
	Class string
	// Status is what a script reads of the page (StatusAttention, StatusClear or StatusUnknown); the
	// JSON of the run list and the Needs attention list carry it.
	Status string
	// Word is the state in capitals ("VERIFIED"), Icon its glyph (a name of icons) and Line one
	// sentence of why.
	Word, Icon, Line string
	// Details are further lines the state calls out (a weakened gate's policy).
	Details []string
	Stats   []Stat
	// Link, with LinkText, is a link the banner carries (the run list's to the Needs attention list).
	Link, LinkText string
}

Verdict is the answer a page opens with: one state in large type, a line under it, and a few key numbers. A state is never carried by color alone: Word and Icon say it too.

type View

type View struct {
	RunID  string `json:"run_id"`
	Kind   string `json:"kind"` // agent.RunKind, or "" when the run has no run:start
	Input  string `json:"input,omitempty"`
	Flow   string `json:"flow,omitempty"`   // the flow name run:start records
	Digest string `json:"digest,omitempty"` // the topology digest flow:digest records
	// State is the agent.RunState, except "halted" for a started run with a live attempt marker
	// and no result (see Halts): a resume returns *agent.OutcomeUnknown, so it is not in progress.
	State     string `json:"state"`
	StateText string `json:"state_text"` // State as the page shows it ("halted: outcome unknown")
	Terminal  string `json:"terminal,omitempty"`
	// EndedAt is when the run's first end marker was written, by the clock of the process that wrote
	// it (agent.RunStatus.EndedAt); empty when the run has not ended or the marker records no time.
	EndedAt string `json:"ended_at,omitempty"`
	Records int    `json:"records"`

	Events  []Event      `json:"events"`
	Halts   []Halt       `json:"halts,omitempty"`
	Gates   []Gate       `json:"gates,omitempty"`
	Graph   *Graph       `json:"graph,omitempty"`
	Conform *Conformance `json:"conformance,omitempty"`
	Proof   *Proof       `json:"proof,omitempty"`
	Notes   []string     `json:"notes,omitempty"` // what the page cannot show for this run, and why
	// Held are the active kill switch stops that hold, or may hold, the run at its next drive (see heldBy);
	// empty when no stop applies or the viewer reads no control run.
	Held      []StopHold `json:"held_by,omitempty"`
	LastNode  string     `json:"last_node,omitempty"`
	FinalText string     `json:"final_text,omitempty"`

	// Live: the page follows the run (live mode): it was read from a store, not an export, and has
	// no end marker. Cursor is the store sequence (agent.Entry.Seq) of its last record.
	Live   bool  `json:"live,omitempty"`
	Cursor int64 `json:"cursor,omitempty"`

	// Verdict is the answer the page opens with (see runVerdict); it is not in the JSON view.
	Verdict Verdict `json:"-"`
	// contains filtered or unexported fields
}

View is everything the run page shows, built from the run's records alone (see Build).

func Build

func Build(ctx context.Context, runID string, recs []agent.Record, flow *Flow) *View

Build reads the run's records into a View. recs is the run's journal in order (header included); flow, when non-nil, is the declared flow the run is drawn against.

func (*View) KindText added in v0.16.0

func (v *View) KindText() string

KindText is the run's kind as the page shows it.

Jump to

Keyboard shortcuts

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