Documentation
¶
Overview ¶
Package run is the engine: what happens between `hyper run <procedure>` and the Journal entry it leaves behind (§6, issue #136).
It takes a loaded repository, a Store handle, the reads the process supplies and the Capabilities' performers, and answers a value the CLI renders. **It reaches no process fact of its own**: no clock, no randomness, no environment variable. Every one of them is threaded through Request, which is what makes a Run's every path in the Store a checked-in constant rather than a value normalised out of a golden (§8, ADR-0047), and run_test.go holds it over this package's own source.
That is a claim about **this package** and not about the tool beneath it, and the difference is worth stating rather than leaving to be discovered. Two packages under it start a git subprocess with the process's own environment, deliberately: the git hyper shells out to is the same git that resolves the credential a checkout left behind (§7, §11), and internal/git holds the one rule that keeps such a subprocess acting on the repository the caller named. So the engine performs no read of its own; what it reaches through does, and where it reaches is the repository root it was handed.
It renders nothing either. What a Step table looks like, what the terminal line says and which exit code an outcome maps onto are §8's and §9's, and they live in internal/cli beside every other surface. What is here is what happened; what is said about it is one package up (ADR-0026).
**The order is §6's fixed order**, and no Step starts until all of it has happened: the pin gate, the lock and the Store's location, all three the CLI's and all three declining before a Run is identified at all; then `run.json`; then the Store schema test, `check` re-run in full, the credential pass and the Secret sink, each of which declines into the entry that already exists; then Step 1. Perform states the order and gates.go states the four in the middle (issues #137, #138).
The lock is the CLI's for the reason the Store's location is: it is a lock on the Store, a Run that cannot take it never opens one, and a Run with no Store has no entry to decline into. Which of the two locks it takes is this package's, though — it is read off the Kinds and nothing else, which is lock.go.
Index ¶
Constants ¶
const ( // CodeCredentialAbsent is a credential a Target declaration names and // the environment does not hold. CodeCredentialAbsent = "credential-absent" // CodeCredentialEmpty is a credential a Target declaration names that // the environment holds and sets to the empty string — a variable an // upstream produced rather than one nobody exported. CodeCredentialEmpty = "credential-empty" // CodeSecretSinkAbsent is a Run reaching a Step whose Operation declares // secret output where the invocation supplied no Secret sink — the one // place a secret value may go, and a Run that produced one with nowhere // to put it would destroy it (§9, ADR-0007, ADR-0148). // // It is the **occasion's** supply like the two above it, and it is a code // of its own for the reason they are two: its remedy is an act nobody // else's is — the same command again with `--secret-out <path>` — which // is §8's fourth remediation class and this code's sole member // (refusal.go, §12). // // It stood down for one release while nothing wrote the file, its place // held by `secret-sink-unwritten`, whose remedy was a different binary: // a sink named and a sink withheld were the same Run then, so a remedy // naming the flag would have ended on another `77` (issue #266, // ADR-0146). The writer is what brings it back and retires the other // (issue #270). CodeSecretSinkAbsent = "secret-sink-absent" // CodeSecretSinkUnfilled is the converse: a Run given a Secret sink // that reaches no Step whose Operation declares secret output, so // nothing would ever be written into it. // // It is one gate's other operand and a code of its own on the set's own // test — a reader handed `secret-sink-absent` here would go looking for // a sink they named. It is the **occasion's** supply like the three // above it, and it is the one member of §8's not-an-edit set whose note // names an artefact edit anyway: `hyper` holds both facts and cannot say // which half is wrong, an author who meant a secret having written the // declaration somewhere it is not read, so the note names the edit first // and the invocation second (§8, §9, refusal.go). // // It did not exist while the silence was the design, and the silence // was defensible: a sink named against a Procedure that produces none // left no empty directory behind (sink.go). What it cost is ADR-0149 — // a sink named, a `secret:` written inside `fields:` and dropped, two // credentials minted and destroyed at exit `0`, with `hyper` holding // both operands at run start and saying nothing (issue #275, ADR-0151). CodeSecretSinkUnfilled = "secret-sink-unfilled" )
The four codes §9 contributes, alike in being neither the environment's nor the artefacts', all four checked before a Run's first Step and all four reported exhaustively rather than at the first (§12, ADR-0007).
All four are the **occasion's** supply: two variables the environment was to hold, one path the command line was to name and one it was not. The third was this *binary's* for one release, while nothing wrote the sink at all and no invocation could rescue a Run reaching a secret-producing Step; the writer returns it to the group it belongs in (ADR-0146, ADR-0148). The fourth is the third's own gate read the other way round (ADR-0151).
They are spelled here because this is where the checks that carry them are. internal/store reports the schema condition and does not name a code, and §4's thirty-two arrive already carrying theirs.
The first two are one gate reading one variable, and they are **two codes rather than one message**, on the set's own test: a reader handed `credential-absent` for a variable that is exported checks the export, finds it, and is out of moves. The remedies differ and §8 holds one remedy per code (refusal.go), so a single code could only ever have offered the wrong one to whichever half it was not written for (§12, ADR-0145). The last two are one gate reading two operands and are two codes on that same test: a reader handed `secret-sink-absent` for a sink they named goes looking for the flag they already typed.
const CodeBoundExceeded = "bound-exceeded"
CodeBoundExceeded is §6's half of the code §4 already fires where the count is authored: an Expansion resolving to more Records than the Step's declared Bound.
It is one code because it is one check — what names a Refusal is the check that declined, never the moment it ran — and it is spelled here rather than imported from internal/artefact for CodePredicateTypeMismatch's reason above: §4's constant names a length a reader counts off the page and this one a count no file holds, and the string is the contract the two sites reach independently, as §4's and §6's own texts do (§4, §5, §6).
const CodePredicateTypeMismatch = "predicate-type-mismatch"
CodePredicateTypeMismatch is §6's half of the code §4 already fires where the fault is authored: an operator handed a **stored** value it cannot compare.
The two halves are one check at two moments, which is why they are one code (ADR-0035). It is spelled here rather than imported from internal/artefact because that package's constant names an authored fault and this one names a value nobody wrote — the string is the contract and the two sites reach it independently, as §4's and §6's own texts do.
const CodeRecordIdentityCollision = "record-identity-collision"
CodeRecordIdentityCollision is the code both of the Expansion's identity comparands carry: two members of one Expansion resolving to one Record identity, and a resolved identity colliding with a series the Store already holds (§6, §7, §12, ADR-0070, ADR-0075).
const CodeRunOnceRecorded = "run-once-recorded"
CodeRunOnceRecorded is §6's second run-time check: a run-once Step the Journal already holds as *ran* or as *attempted, outcome unknown*.
It is one of the few members of the closed set that require a Step to have been reached at all, and like every one of them it declines **before a call goes out** — a guardrail that declines after one is a halt and has no `error_code` to carry (§12, ADR-0072).
Variables ¶
var ErrInterrupted = errors.New("interrupted: no further Step was started, and this Run closed its own entry")
ErrInterrupted is what stopped a Run that drained: the Run's own Fault, and what the surface reads to know that the code §12 fixes for this stop is the signal's rather than the outcome's (§6, §12, ADR-0015).
It names no signal. Which one arrived decides `130` from `143` and that is §12's mapping, made where the signal was caught; what this package holds is that the Run was stopped rather than resisted — the two are both `failed`, and a fault a reader can act on is the whole of what a `failed` Run says beyond its outcome (§9).
var ErrSyncFailed = errors.New("the Store could not be synced: this Run's entry did not reach the remote, and an effectful Run does not touch the world against a record it cannot write")
ErrSyncFailed is an effectful Run whose push of `run.json` did not land: its Store sync, and the earliest moment such a Run could have known it would be able to record what it does (§7, ADR-0083).
It is the third way a Run loses the Store, beside the lock and the push, and it is `failed` at `75` for their reason: what it would take to clear it is time rather than an act of anyone's, which is exactly what sorts `75` from a Refusal's `77` (§12, ADR-0061). That mapping is the surface's, one package up — this one holds no exit code and maps none — and what stands here is the condition a reader can act on.
A read-only Run never reaches it. Its sync is the fetch one layer up, which it attempts and tolerates, and it pushes nothing until its end (ADR-0083).
Functions ¶
func LockMode ¶
func LockMode(loaded repository.Loaded, procedure string) store.LockMode
LockMode is the lock the named Procedure's Run holds for its duration: **exclusive where it contains any effectful Step, shared where every Step is `read`**. So a five-minute monitoring cadence is not starved behind a forty-minute provision, and two monitoring cadences are not starved behind each other.
It is decided from the Kinds `check` already computes and **before any Step runs** — before the Store is reached at all, in fact, since the lock is a lock on the Store and a Run that cannot take it never opens one. That is what makes this a walk over reviewed text with no world in it: the Kind is read off the Operation the Step binds and never off the Step, a Kind being declared per Operation in a Manifest and never inferred (ADR-0025).
**A Step whose Kind cannot be read is exclusive**, and so is a Procedure whose Steps this walk could not reach in full. An unresolvable binding leaves no Kind to judge; an invocation naming nothing, and a cycle, leave Steps the walk never saw (sequence.go) — and a Run whose blast radius cannot be read is not a Run that may share the Store. None of them ever gets as far as its first Step: every one is `check`'s to refuse at Run start. The lock is taken before that, so what it does with them is a fact of its own rather than one that path makes unreachable.
A Procedure name that resolves to nothing is exclusive on the same reading. It is unreachable from the CLI, which resolves the positional against the namespace first (§9, ADR-0060), and the safe answer costs nothing where nothing can reach it.
Types ¶
type Answer ¶
type Answer struct {
// Started is the instant the Run began, which is the instant every
// relative predicate in it resolved against (ADR-0034). It is here
// because §8's Refusal renders the gloss — `older_than: 14d resolved
// to …` — and the supply for one is a Run's start and nothing else: a
// surface with no Run renders no gloss, and a surface with one derives
// it rather than reading a member no file holds.
Started time.Time
// Run is the Run's id, and Identified says an entry was written under
// it. The pair is §8's *where no entry was written the id is absent, and
// its absence is the fact*: two paths decline before a Run is identified
// at all, and on both the terminal line names nothing to look up.
Run store.RunID
Identified bool
// Outcome is §12's triple, and every Run reaching this package ends in
// exactly one member of it.
Outcome store.Outcome
// Steps is one entry per Step that reached a Disposition, in the Run's
// written order — which is the Step table's order and the `<nnnn>` the
// entry names its files by (§8, §12).
Steps []Step
// Withheld is the position of the Step a rehearsal stopped at, and zero
// where the Run withheld nothing — which is every Run that is not a
// `--dry-run`, and every rehearsal that reached the end of a Procedure
// with no effectful Step in it (§9, ADR-0010).
//
// It is the position and not a Disposition of its own, because §12's
// seven are closed and the withheld Step is one of them: the Run ended
// before it, it wrote no file, and *never reached* is what an entry
// reads back from that silence (§7). What this carries is *it stopped,
// and here is where* — which the entry holds as the first Step of the
// run of silent ones and the surface would otherwise have to re-derive.
//
// **Every surface reads it from here**, through Withholds below: the
// page's sentence beneath the Step table, and the `withheld` member on
// that Step's row, which is what carries the same fact to `--json` and
// to MCP (§8, §9, ADR-0091, issue #206).
Withheld int
// Provenance is the Run-wide half: what `run.json` carries, and what the
// Run-wide `provenance` row renders (§7, §8, ADR-0043).
Provenance store.RunProvenance
// Refusal is the checks that declined, in the order `check` prints them,
// and is non-empty exactly where Outcome is OutcomeRefused. It is one
// array and never several Refusals: a Run has at most one Refusal ever,
// the outcome being terminal, and the members are the checks one phase
// evaluated together (§7, §8, ADR-0061).
//
// What the terminal line and the `outcome` row name is the **first**
// member's `error_code`, derived where it is rendered and stored nowhere
// (§7, §8).
Refusal []Refusal
// Fault is what stopped the Run, and nil where nothing did. It is
// narration's — a failure carries no `error_code` (§9, §12) — and it is
// the whole of what the surface has to say about a `failed` Run beyond
// the outcome itself.
Fault error
}
Answer is what a Run did, in the shape the CLI renders (§8).
It carries no exit code and no rendered line. §12 maps an outcome onto a code and §8 states what the line says; a value that carried either would be a second representation of a fact one layer up, which is the rule the Store itself is written under (§7).
func Perform ¶
Perform runs the Procedure and answers what it did.
**The order is §6's, and it is the whole of this function.** What the milestone has not built declines first, before anything is minted; then the code branch is read, because a Run that cannot name a revision has no Provenance to write and nothing to write it on; then the Run is identified and `run.json` goes down; then the Steps, in written order; then `outcome.json`; then the push.
It answers rather than returning an error, because a Run that stopped is still a Run that happened: what it did before it stopped lives in its Records and its Dispositions rather than in its outcome (§6), and every one of them is on the Answer.
func (Answer) Withholds ¶
Withholds is *this is the Step the rehearsal stopped at*, asked of one Step of this Answer's own Steps (§9, ADR-0091, issue #206).
It is a method rather than a comparison each surface writes because the page and the row must be **one fact and not two that have to agree**: the sentence beneath the Step table and the `withheld` member on the row are two renderings of this predicate, and a copy of it is a place they can drift apart (§8, ADR-0026).
A Run that withheld nothing carries zero, and no Step is at position zero — so a Run the world resisted answers false everywhere, however many *never reached* Steps it left behind it. That is the discrimination the fact exists to make: the withheld Step's Disposition is *never reached* like theirs, and nothing but this says which one it was.
type Narrator ¶
type Narrator interface {
// Began is the Run naming itself, before its first Step. It is the one
// place a Run's identity reaches an operator whose process is killed
// outright, which is why it is narrated rather than left to the
// terminal line (§9).
//
// A surface where that is not the one place sends nothing, which is a
// rendering like any other: a Narrator decides what an event says, and
// saying nothing is one of the answers.
Began(run store.RunID)
// Uncommitted is the Run saying the code it is about to perform is in
// no commit: an artefact it read differs from `HEAD` or is untracked,
// which is the same fact `repo_dirty` marks on the entry (§7).
//
// It fires **before the first Step** and where that fact stands, and
// nowhere else. What it costs is what the entry cannot say for itself
// in time: every revision this Run is about to record is a blob id
// nothing ever wrote, so a later `review` of any of those artefacts
// opens at nothing and the Comparison's baseline is gone (§8,
// ADR-0119, issue #239).
Uncommitted()
// Reached is one Step boundary: the Step's position, how many the Run
// holds, and its authored id.
Reached(position, of int, id string)
}
Narrator is a Run's progress as it happens, and it is three events because §9's narration is three lines: the Run naming itself before its first Step, the warning where the code it is about to run is in no commit, and one line per Step boundary, in both modes, always on.
**The middle one is the only conditional line**, and it is an event rather than a member of the first because that is what keeps the surface's job the surface's: the engine has read the code branch by the time it fires and knows the fact, and whether the fact is worth a line — and what the line says — is a rendering (§9, ADR-0119).
It is an interface rather than a stream this package writes to, because what those lines *say* is a rendering and renderings live in internal/cli. The engine reports the boundary; the surface writes the words. It carries no machine contract and has no `--json` variant, so nothing here derives from it and nothing reads it back.
**The second implementation is what the interface was for.** On the MCP surface a Step boundary is a `notifications/progress` and the Run naming itself is nothing at all, because the id reaches that caller in the summary line and in `run_id`; the engine reports the same two events either way and knows about neither rendering (§9, ADR-0092, cli.notifications).
type Narrowing ¶
type Narrowing struct {
// Line is where the operand is authored and Field its path in §8's
// remediation notation. They are the predicate's own coordinate rather
// than the Bound's: the two rows of `EDIT ONE OF` are two edits, and
// they are on different lines.
Line int
Field string
// From is the operand as authored and To the rung proposed.
From, To string
// Expansion is what the proposal resolved to — the count the page
// carries, and the whole reason this read was performed.
Expansion int
}
Narrowing is the narrowed selector as the `EDIT ONE OF` table renders it and the `remediation` row carries it: where the operand is, what it says now, what it would say, and what that would expand to (§8).
It is the **first relative operand** the selector carries and no other. §12's eleven operators are not all narrowable in a direction a tool can name — an `equals` has no next rung, and a `starts_with` narrowed by a character is a guess about names rather than arithmetic — where a duration has an order and a direction that shrinks the set. So the proposal exists where the arithmetic does, and where it does not the table renders the Bound row alone rather than inventing a second one.
type Refusal ¶
type Refusal struct {
store.RefusalMember
// Operation and Target are the binding of the Step this check cites,
// and are empty on a Refusal that cites no Step.
Operation, Target string
// Narrowed is the second remediation §8's `EDIT ONE OF` table renders
// beside a Bound, and nil on every check that has no second edit to
// offer — which is every one of them but `bound-exceeded`, and that one
// only where its selector carries an operand a rung can be proposed for
// (§8, narrow.go).
//
// It rides here rather than in the entry for the reason `resolved` rides
// on a row: it is derived, it is a hypothetical, and a Store holding one
// would be a Store holding a count no Run ever acted on (§7, ADR-0034).
Narrowed *Narrowing
}
Refusal is one check that declined a Run: everything its Store counterpart carries, plus the two members a row adds where it cites a Step (§7, §8).
The two are on the wire and never in the entry, and that is §7's own split rather than an omission: `operation` and `target` are what the Step the check cites was bound to, and the entry already holds them on the Step's own file wherever a Step file exists. A Refusal before Step 1 writes none, so a consumer reading the row is handed the binding and a consumer reading the entry reads the artefact that made it.
type Request ¶
type Request struct {
// Repository is the load every name in the Run resolves against, and
// RepoRoot is where it was read from — the second being what the code
// branch's revisions are read out of.
Repository repository.Loaded
RepoRoot string
// Store is the record, already located. Locating it is the caller's,
// because a Run that cannot find one Refuses `store-absent` before it
// has an id to refuse under (§6, §7).
Store *store.Store
// Procedure is the top-level Procedure's name, already resolved against
// the repository: a positional matching nothing is a usage error and
// never reaches here (§9, ADR-0060).
Procedure string
// Trigger is what caused this Run and where it happened, already read
// off the environment. It is filled rather than derived here for the
// reason this package reads no environment variable at all — and
// nothing in the engine reads it back, recording where a Run happened
// being a fact about the occasion rather than an authority axis (§5,
// §7).
Trigger store.Trigger
// DryRun says this Run is a rehearsal. It is carried into the entry on
// every Run, false included (§7, ADR-0001).
//
// **A rehearsal performs the reads it reaches and stops at the first
// effectful Step** rather than simulating one (§9, ADR-0010). Nothing
// about that Step is simulated, predicted or described: `hyper` has no
// plan, and a surface saying what a `mutate` *would* do would be the
// prospective counterpart the Comparison deliberately is not. The stop
// is Perform's, and Withheld on the Answer is the Step it stopped at
// (issue #155).
//
// The places the marker is read back are already built, and both read
// it off an **entry** rather than off this member: an entry a rehearsal
// wrote is evidence that a rehearsal happened and evidence of nothing
// else, so a Step's identity digest filters it out (step.go) and so does
// run-once Repeatability (once.go). Getting the second wrong is what the
// exception to the absence rule is bought against — a rehearsal counted
// as evidence would permanently refuse every run-once Step in the
// Procedure it rehearsed (§7, §8, ADR-0001).
DryRun bool
// Version is the binary's, and it is Provenance's `hyper_version`. It is
// always a release string: the pin gate refuses any binary whose version
// differs from the repository's in either direction, so there is no
// development form to write (§7, §11).
Version string
// Now is the clock. Every instant the Run records comes through it, so a
// fixture's entry is reproducible and the dates a golden holds are the
// case's own.
Now func() time.Time
// Mint mints the Run id at the instant it is handed. It is threaded
// rather than called for the reason the clock is, and one more: a Run id
// lands on the terminal line, in the `outcome` row, in `run.json` and in
// every Store path a Run writes, so an id minted here would make every
// golden of a Run unassertable (§8, ADR-0047).
Mint func(now time.Time) store.RunID
// LookupEnv is the environment, read. It is threaded for the reason the
// clock is — the engine reaches no process fact of its own — and it is
// reached exactly once per credential slot the Run's bindings require,
// at the credential pass §6 puts before Step 1 (§6, ADR-0007).
//
// It answers a value **and whether the variable is set at all**, which
// is os.LookupEnv's shape rather than os.Getenv's: presence is the whole
// of what the gate asks, and a variable set to the empty string is
// present (§9).
LookupEnv func(string) (string, bool)
// Environ is the environment read whole, and it is reached for exactly
// one thing: a `shell` Operation's child inherits it, less every
// credential-slot variable in the repository (§3, §11, issue #142).
//
// It is a second read of one subject rather than a widening of the
// first. LookupEnv answers *what does this name hold*, which is the
// whole of what the credential pass asks; composing a child's
// environment is a subtraction, and a set of names nothing enumerates is
// not a set anything can subtract from.
Environ func() []string
// SecretSink is the Secret sink: the directory a Step whose Operation
// declares `secret:` output writes its values into, and "" where the
// invocation named none — which on a Run reaching such a Step is
// `secret-sink-absent` at §6's gate (gates.go, sink.go, ADR-0148).
//
// **It arrives absolute**, resolved by the surface that read it against
// that surface's working directory, for the reason every other read here
// is threaded: a path resolved in this package would be resolved against
// the process's own directory, which is the one fact the engine reaches
// for nothing (§9, internal/cli/run.go).
//
// The three faults the path itself can have are the command line's and
// are refused there, before this value is built: `-`, a path inside the
// repository working tree, and a path that is already there — the sink
// being a directory `hyper` makes, so that every file under one is this
// Run's (§9, ADR-0148).
//
// It was removed for one release and this is its return. Nothing wrote
// the file, so the field was the occasion's supply arriving here to be
// dropped — which is how a Run came to complete and destroy the value it
// was given a sink for (issue #266, ADR-0146). What earns it back is the
// writer.
SecretSink string
// Dial and Exec are the two Capabilities' performers. Neither is reached
// for: internal/capability is handed one, so a case exercises a real
// handshake against a server standing in the test process and a real
// child against a script a fixture checked in (§5, issues #133, #142).
Dial capability.Dial
Exec capability.Exec
// Interrupted says the first interrupt has arrived, and it is the whole
// of what this package knows about signals: which signals are watched
// for, what they exit with and when a second one kills the process are
// the surface's, one package up (§6, §9, ADR-0015, issue #145).
//
// It is a function for the reason every other process read here is one
// — the engine reaches no process fact of its own — and it answers
// rather than blocks, because the one question a Run asks about a stop
// is *has one arrived by now*: it is asked where the next Step would
// start and nowhere else, so a Step in flight is never asked to stop
// and never told to (Perform).
//
// **Being a predicate rather than a signal is what let a second surface
// arrive without a second mechanism.** A `run` reached through the MCP
// server has no terminal behind it and catches no signal at all; what
// stops it is the client cancelling the request, and the mapping is
// this predicate reading that call's context. The composition of the
// two is the surface's, one package up, and nothing here can tell which
// of them answered (§6, §9, ADR-0015, ADR-0092, cli.stopping).
//
// It may be nil, which is a Run nobody can stop: a `hyper` compiled
// into a test binary has no handler installed and no call to cancel.
Interrupted func() bool
// Narrator is where progress goes as it happens. It may be nil, which is
// a Run nobody is watching.
Narrator Narrator
}
Request is everything a Run is performed from.
The four process reads are functions rather than resolved values, which is cli.Process's own rule one layer down: a read a Run never makes is a read that never happens, and a Run of one `read` Step starts no child and dials once.
type Step ¶
type Step struct {
// Position is the Step's place in the Run's written order, the first
// Step 1.
Position int
// ID is the Step's authored `id:`, Kind its Operation's declared Kind
// held rather than resolved, and Disposition what became of it (§7).
ID string
Kind store.Kind
Disposition store.Disposition
// Path is the invocation chain this Step was reached through, and empty
// on a top-level Step. It is the same member the Step file carries, and
// it is what the Step table renders a nested Step under (§7, §8).
Path string
// Records is the size of the identity set — what the Step concluded
// about, and never the versions it wrote (ADR-0030) — and Concluded says
// a set exists at all. Three of §12's seven Dispositions conclude about
// nothing, and the dash that renders for them is what tells them from
// the *ran* Step whose set is written empty (§8).
Records int
Concluded bool
// Expanded is how many Record identities the Step's calls **reached**,
// and it is written **only where the Step stopped short of them** — a
// `read` Expansion that drained and then halted (§6). It is what
// `n of m` is read against: `n` is Records and `m` is this, and the
// Records between them are the ones unaccounted for (§7, §8, issue
// #140). A Step carrying no `over:` resolved no selector and makes its
// call under a set of one, so a halted one is `0 of 1`.
//
// It counts Records and never the Expansion's members, which are the
// same number only where an Operation projects one Record per member.
// A `series` response whose tenth member's identity path did not resolve
// is one member of an Expansion and ten Records reached, and the column
// reads `9 of 10` — the entry says expanded to one beside it,
// `expanded_to` being what the *selector* resolved to and this being
// what the answers held (issue #144).
//
// Zero is *nothing stood short*, which is every Step that accounted for
// everything it reached. It is written from the drain rather than
// derived from Records, because *unaccounted for* is a fact about
// Records that were never concluded about and the two counts are equal
// for other reasons too — an identity two members resolve to is one
// member of the set and two Records reached, which is a collision §6
// halts on rather than a Step that stopped short (ADR-0070, ADR-0072).
//
// It is a count and never the names. Which Records are unaccounted for
// is `expanded_to`'s and nowhere else, and a Step value carrying them
// would be the second place a surface could read them from (§7, §8).
Expanded int
// SecretsSkipped is how many of the Records this Step concluded about
// it made **no call** for, on a Step whose Operation declares `secret:`
// output — and zero on every other Step in the tool (§8, §9, ADR-0150,
// issue #273).
//
// It is the count of Secret sink entries that are **absent for a
// reason**. A `skip-if-recorded` member the Store already holds is
// concluded about without asking the world for it, so there is no value
// to write and the sink holds no directory for that Record; the absence
// is the answer, and this is the number the surface says it with (§9,
// ADR-0148).
//
// **It is scoped to a secret-producing Step and is not the skip split.**
// How a mixed Step divided into members that called and members that
// skipped is derivable — a member that ran under this value always mints
// a version, a standing head having skipped it — and ADR-0056 declined
// to carry it. What is not derivable from any surface is which sink
// entries are missing and why, this being the one fact about a Run that
// is about a directory on the operator's own disk.
//
// A wholly skipped Step and a mixed one both carry it: the Disposition
// tells a reader the first skipped and says nothing at all about the
// second, whose `ran` and whose count are what a Step that wrote every
// value carries too.
//
// **It is a count against Records above**, so it is zero wherever
// Concluded is false: *attempted, world untouched* carries no set, and a
// Step that skipped one member and whose next request provably never
// left is that Disposition (ADR-0062). On a halted Step carrying `n of
// m` it counts the skips among the `n` — the members the halt never
// reached are neither skipped nor called, and they are Expanded's.
SecretsSkipped int
// Provenance is the Step's half: what the Step file carries, and what
// that Step's `provenance` row renders (§7, ADR-0043).
Provenance store.StepProvenance
}
Step is one Step of a Run as a surface reads it back.