Documentation
¶
Overview ¶
Package cli is the third seam #87's Implementation Decisions name: argument handling, path stats, the two renderings, ordering, filtering, exit codes, stream discipline. It owns no rule of its own — every problem it prints comes from the load (internal/repository, over internal/yamlsubset's own grammar), the pin gate (internal/pin), and an artefact's own schema (internal/artefact).
Index ¶
- Constants
- func Child(ctx context.Context, argv []string) *exec.Cmd
- func Commands() []string
- func Globals() []string
- func MCPDispatch(process Process, facts version.Facts) mcp.Dispatch
- func MCPServer(process Process, facts version.Facts) *mcp.Server
- func Main(args []string, stdout, stderr io.Writer, process Process, facts version.Facts) int
- func OutsideTree() []string
- func RunChanges(args []string, to destination, process Process, wd, binaryVersion string) int
- func RunCheck(args []string, to destination, lookupenv func(string) (string, bool), ...) int
- func RunCompact(args []string, to destination, process Process, wd, binaryVersion string) int
- func RunCompletions(args []string, stdout, stderr io.Writer) int
- func RunInstall(args []string, to destination, process Process, wd, binaryVersion string) int
- func RunMCP(args []string, narrate io.Writer, process Process, facts version.Facts) int
- func RunOperation(args []string, to destination, lookupenv func(string) (string, bool), ...) int
- func RunProbe(args []string, to destination, process Process, wd, binaryVersion string) int
- func RunProject(args []string, to destination, process Process, wd, binaryVersion string) int
- func RunProvider(args []string, to destination, lookupenv func(string) (string, bool), ...) int
- func RunProviders(args []string, to destination, lookupenv func(string) (string, bool), ...) int
- func RunRecords(args []string, to destination, process Process, wd, binaryVersion string) int
- func RunReview(args []string, to destination, process Process, wd, binaryVersion string) int
- func RunRun(args []string, to destination, process Process, wd, binaryVersion string) int
- func RunRuns(args []string, to destination, process Process, wd, binaryVersion string) int
- func RunShow(args []string, to destination, process Process, wd, binaryVersion string) int
- func RunStore(args []string, to destination, process Process, wd, binaryVersion string) int
- func RunTargets(args []string, to destination, lookupenv func(string) (string, bool), ...) int
- func RunVersion(args []string, stdout, stderr io.Writer, facts version.Facts) int
- func Shells() []string
- func StoreSubVerbs() []string
- func Streams(stdout, stderr io.Writer) destination
- func Tree() []string
- type Notify
- type Process
Constants ¶
const ( ExitClean = exit.Clean ExitProblems = exit.Problems ExitUsage = exit.Usage ExitStoreLost = exit.StoreLost ExitRefused = exit.Refused ExitInterrupted = exit.Interrupted ExitTerminated = exit.Terminated )
The exit codes §12 closes at seven members, under the names every call site in this package uses: a command answers `ExitRefused`, and what that number is is internal/exit's to say.
**The numbers moved one package out the day a second surface had to read the same closed set.** §9's MCP server has no exit code to hand a client, so it maps each of these onto an envelope — the answer, a protocol error, or a Refusal rendered whole — and a mapping that spelled `77` for itself would hold a second opinion about what `77` is. The names stay here because a command returning one is this package's grammar, and because the alternative was five hundred call sites re-spelled to move a constant (§9, internal/exit, internal/mcp, issue #196).
Variables ¶
This section is empty.
Functions ¶
func Child ¶
Child is Process.Exec, which states what the two launch decisions here are for. This is how they are made (§5, §6, issue #142).
Setpgid is the process group, and it is set rather than the child being left in `hyper`'s own: what a deadline kills is then the whole tree the argv started, a command that forks being the ordinary case rather than the exotic one. It is also what makes draining true of a `shell` Step at all — in `hyper`'s group a terminal's interrupt reaches the child directly and it dies at once, so the Step in flight would not finish and the drain would be a sentence the implementation contradicts (§6).
Cancel is replaced because exec.CommandContext's own cancellation signals the leader alone, which would leave the group Setpgid just made — so the signal goes to the negated pid, which is the group, and it is SIGKILL with nothing before it. SIGKILL rather than a SIGTERM and a wait, because the wait is a guessed constant on a Provider that knows nothing whatever about the command, which is the ground `concurrency:` is 1 on (§6, ADR-0045).
argv is exec'd directly with no shell between the artefact and the process, and its head is read off it without a question asked: the authoring format admits no other shape and `check` refuses a command without a literal head long before a Run reaches a Capability (ADR-0051).
It sits in this package rather than in the binary's own main because the corpus drives it: a case that launched a child through a stand-in launcher would leave the process group and the SIGKILL unchecked, and two spellings of the two decisions is where the day comes that the binary and the suite make them differently. cmd/hyper wires this value and adds nothing to it.
func Commands ¶
func Commands() []string
Commands is the whole surface as one list: §9's sixteen, then the three outside the tree. It flattens the same groups the usage page renders, which is what makes the two one list read two ways (usage.go).
func Globals ¶
func Globals() []string
Globals is the three configuration flags, in the spelling a caller types.
func MCPDispatch ¶
MCPDispatch is what stands behind every tool: one call's argv run through the same table `hyper` dispatches a command line through, against a destination that holds no stream (destination.go).
It is exported for MCPServer's own reason, one layer down. The drivers that reach past a golden drive a corpus case with one more input supplied, and the input a **cancelled call** supplies is the call's own context — which is the server's to make and not a caller's, so a driver that wanted to hold it had nowhere to stand. A driver stands the same tool set over this dispatch with an observer in front of it, exactly as the corpus stands the same server with a tee on the wire (mcp.Server.Call, mcp_cancelled_test.go, issue #202).
func MCPServer ¶
MCPServer is the MCP server over this process: the tool set internal/mcp states, at the binary's own version, over the dispatch below.
It is exported for Streams' own reason — a caller outside the package that drives the surface rather than the command line needs a door, and this is it — and the golden corpus is that caller: a `call` case is driven through this server over in-memory transports, so what the corpus exercises and what a client starts are one server assembled one way (golden_mcp_test.go).
It is not spelled `Server`, and the reason is the word rather than the package: §9 and §13 each spend a paragraph refusing `serve`, and ADR-0088 refuses it again for the invocation. A bare `Server` here would put the word back at the one place a reader looks up what the binary does — which is the refusal working as stated rather than an inconvenience of it.
func Main ¶
Main is hyper's one entry point: it takes the complete argv and returns the exit code, and which command runs is decided here rather than in cmd/hyper/main.go (issue #107).
The gate's own reasoning is why the dispatch followed it in — gate.go states it, and it reaches the thing that decides which commands call the gate at all. With four commands landing in milestone 2 and eleven more to come, dispatch is not a detail of `main`; it is the surface §9 fixes, and it belongs on this side of the package boundary where the golden harness can reach it. That the harness does not yet drive it is #108's, which collapses the corpora onto this entry point; #107 is the half that makes the change easy.
Everything a command reads from the process is a parameter, which is the property #100 established and this must not lose: the arguments, the reads process.go states, and the facts the build stamped. Nothing in the body below reaches the process for itself, which is what makes the whole dispatch exercisable without a subprocess.
process is those reads as one value rather than as one parameter each, which is the only thing issue #134 changes: the clock reached this signature loose one milestone earlier, three more reads land in this one, and six of them threaded singly is a parameter list a reader counts instead of a type they open. What each member is and why it is threaded is process.go's to say; that a command reaches for none of them itself is this dispatch's.
Getwd is called on the repository commands' arm and nowhere else, because that exemption is a property of this dispatch and not of the commands behind it. `version`, `completions` and `mcp` are the three cases that resolve no working directory and call no gate — an exemption expressed as a path not taken (§9, ADR-0020) — so a working directory that cannot be read does not stop `hyper version`. `mcp` is the one of the three that is not exempt from the gate at all: it never reaches a repository because starting a server is not an act on one, and every tool it carries gates exactly as its command does (ADR-0088). `project` calls no gate either and is not one of them: it resolves a repository like every other command in the table below and declines to compare itself against the pin it is about to write, which is an exemption the command states rather than one this dispatch grants (§11, issue #178).
facts is threaded whole rather than the bare version string it carries: RunVersion needs all of it, the gate needs the version out of it, and passing the value keeps Main deterministic under test.
The two writers are assembled into the CLI's destination here and threaded down as one value: every command behind the dispatch is handed where its answer goes rather than the streams that answer is made of, which is what leaves room for a second destination that is not a pair of streams at all (destination.go, issue #194). Which form it takes is not decided here — the flag that names one is read by the shared parser, which is the only reader of it there is (flags.go).
func OutsideTree ¶
func OutsideTree() []string
OutsideTree is the three commands that stand outside the sixteen.
func RunChanges ¶
RunChanges implements `hyper changes [procedure]` — §8's Comparison: which two Runs are being compared, everything the header says about them, and the three tables and the `TOTALS` line beneath it (issues #167, #170 and #171).
**The derivation is `internal/compare`'s and this is flags, the lookups and the page.** That package opens no file, starts no subprocess and reads no clock: it takes the two Journal entries, the Store reads those entries need and the artefact bytes at both revisions, and answers the ordered row list `internal/render` writes twice (changes_code.go).
**The order past the gate is the positional and then the Store**, which is §9's general rule and the one `show` is the exception to: a Procedure name resolves against the working tree, so a typo is `2` on a repository with no Store at all, and `store-absent` Refuses `77` only once the name is known to be one (§9, ADR-0060).
**`--target`, `--kind` and `--limit` narrow the two Record tables and nothing else.** The header orients a reader between two Runs, and a narrowing that cut it would answer a question about a window the caller did not ask about; `THE CODE MOVED` is outside them for a second reason — all three range over the identity axis, which is the axis the Record tables order on and the axis a code fact has no coordinate on, and narrowing what a reader looked at may not narrow what they are told changed (§8, §9).
`--kind` is validated at the flag rather than at the table, which is where a third name becomes a usage error instead of an empty answer, and the cap is applied to one row list before either rendering (ADR-0026).
It is not a Run: it writes nothing, terminates its stream with `result` rather than `outcome`, and exits 0 whatever outcomes the entries it named record — the exit code is this invocation's and never any Run's.
func RunCheck ¶
func RunCheck(args []string, to destination, lookupenv func(string) (string, bool), wd, binaryVersion string) int
RunCheck implements `hyper check [path...]`. wd is the working directory repository-root resolution walks up from, and that is the whole of what this command reads it for: a path positional is read against the repository root the walk arrives at, never against wd itself (ADR-0089). lookupenv reads HYPER_REPO_DIR and NO_COLOR; binaryVersion is what the pin gate compares against hyper.yaml's pin. All four are passed in rather than read from the process directly, so the whole command is exercisable without a subprocess.
func RunCompact ¶
RunCompact implements `hyper compact` — the second command to touch the record, and the first thing in the tool that removes anything (issue #131).
What it removes is `internal/store`'s predicate and not this file's: for every Observation series the branch holds, a version is removable exactly where it is not the Head, not the series' first version, and older than `retention:`. What this file decides is everything around it — which policy is in force, which Refusals precede the work, and what the two surfaces say afterwards.
The order is §9's and is the same everywhere: the repository root, the gate, then the command's own work. Past the gate it is the Store before the policy, because the two answers are not alike — a repository with no branch is a guardrail declining at 77 and a repository with no policy is a clean run that removed nothing, and a command that read the policy first would answer *there is nothing to remove* to a caller who has no Store at all.
It is not a Run. It writes no Journal entry, terminates its stream with `result` rather than `outcome`, and can never exit 75: that code is a Run that lost the Store, and this command has no outcome triple to map onto (§9, §12).
func RunCompletions ¶
RunCompletions implements `hyper completions <shell>` — the second of the three commands standing outside §9's tree of sixteen, and the first time that tree reaches a shell at all (issue #104).
Like RunVersion it is a sibling of RunCheck taking neither the environment nor a working directory: §9 exempts it from the version pin gate, and the exemption is stated in the signature rather than enforced by a branch, so shell setup in a dotfiles bootstrap works before any repository exists (ADR-0020). Nothing here reads a file or reaches a network, on any path.
Exactly one positional. Naming nothing is a usage error like it is everywhere else, and so is naming two (ADR-0060). The known set is matched byte-exact and case-sensitively, consistent with how every other name in the tool is matched (§9): `hyper completions BASH` exits 2.
func RunInstall ¶
RunInstall implements `hyper install <ref>` — the sixteenth of §9's sixteen, and the single point at which third-party data enters the repository (§9, §11, issue #187).
**One act, two reads, one file.** It parses the ref against the grammar ADR-0087 fixes, fetches the Manifest at it, fetches `checksums.txt` in the ref's own directory, verifies the bytes it fetched against the line naming the ref's basename, and writes `providers/<basename>` with an `origin:` block appended recording the ref it resolved and the digest it verified. Nothing is written on any other path.
**The ref grammar is decided with no network reached**, and an invocation outside it is §9's usage error: exit `2`, no `error_code`, no row stream, the rendering to stderr — the shape the other eight positionals already take, and decidable offline because that is the property ADR-0060 keeps `2` for. What a ref is, and why `hyper` names no registry, is ADR-0087's; what is here is where the grammar is applied.
**It runs no static pass before writing, and that is load-bearing rather than an omission.** §4 states that an Extension the repository never installed lands at `check` as `artefact-absent` on the Definition's `provider:`, with no network reached — so the repository you install into is very often one that does not check, and the thing you are installing is the repair. A pre-write pass, which is `project`'s rule one command over, would make this command unrunnable exactly when it is wanted. It follows that `install` may write a file that immediately fails `check`, and that is the design: the four Extension codes stay `check`'s (§4, §11).
**The path comes from the ref and not from the Manifest.** It is *digest only, never intent*: the bytes are not parsed before they are written, and a command reading a `provider:` key to choose a filename would be deciding on a parse it has no business making. `name-mismatch` already pins a Manifest's `provider:` to its file's basename, so a Manifest published under a filename that disagrees with its own name is reported by `check`, positioned, at the `provider:` scalar, the moment the file lands (§4, §11, ADR-0004).
**It stands behind the pin gate like the other fifteen.** `project` is the one exemption and it is exempt for being the pin's only writer (§9, §11, ADR-0020).
It takes the whole Process rather than environmentOnly's lookup, for `project`'s reason: it dials, and Dial is the member that says so. It reads no clock, mints no id and starts no child, and its signature says that too by what it never reaches for. There is no `--limit`, it naming no namespace to range over, and no `--dry-run`, `check` already reporting digest drift and the diff being the rehearsal; the three globals apply with no fourth (§9).
func RunMCP ¶
RunMCP implements `hyper mcp` — the invocation that starts §9's second surface, and the **third command outside §9's tree of sixteen**, beside `version` and `completions` (§9, ADR-0088, issue #195).
**It takes no arguments at all.** No `--repo-dir`, no `--json`, no transport flag, no port: an argument here would be a per-server setting, and the layer that has none is the point. Not even the three globals are offered — a `--repo-dir` would name a repository nothing at this layer reads, and there is no answer here for `--json` or `--no-color` to present — so anything on the line is a usage error decided from the argument list alone, before a server exists (§9, ADR-0014, ADR-0088).
**It resolves no repository, and it is not a fourth exemption from the pin gate.** The invocation is not the act: what acts on a repository is each tool the server carries, and every one of them resolves one and passes the gate exactly as the command it carries does, at the moment it resolves one (ADR-0088). What the process fixes is *which* repository — `HYPER_REPO_DIR` where the environment sets it, otherwise the walk up from the working directory bounded by the git root, both of them the **process's**, fixed by the client that started it. A client that wants a second repository starts a second server, which is what one process per client already means.
**The stdout it is not handed is the whole of its stream discipline.** §9's *stdout is the answer* is not true of this process — the frames are — so the signature takes the narration and nothing else: there is no writer here to hand a command, and the destination behind every tool has none either (destination.go, mcp.Serve). A usage error still writes a human sentence, and it writes it where every human sentence goes.
func RunOperation ¶
func RunOperation(args []string, to destination, lookupenv func(string) (string, bool), wd, binaryVersion string) int
RunOperation implements `hyper operation <provider> <operation>` — §9's third discovery question, *how do I call it*, and the one place in this milestone where bytes matter rather than facts. It writes the Manifest lines declaring that Operation, verbatim, then the terminal row, and exits 0.
Verbatim is the whole of it, and why is artefact.OperationSource's to state: what the range is, and what a re-encoding of it would silently break. What is this command's is that everything else in the milestone reads a parsed node and renders a value, and this finds a range in a file and copies it — so the range is that reader's, and the whole of what this adds to it is the two lookups and the two messages they write.
It is `provider` in every other respect: the same globals, the same gate before the load, the same stream discipline, and the same two properties — it cannot exit 1, reporting facts rather than problems found (ADR-0064), and it reaches nothing: no network, no credential, no Store, no invocation. It takes no --limit either, for a reason of its own: it names one Operation, so there is no result set at all for a cap to cut (§9).
The two positionals resolve in order and against two different namespaces, which is why a bad name has two messages rather than one. The Provider name resolves against the repository's Provider namespace; the Operation name against that Manifest's own Operation namespace, which does not exist until the first has resolved — so a bad Provider is reported and the Operation lookup is never attempted.
func RunProbe ¶
RunProbe implements `hyper probe <provider> <operation>` — the smallest complete path through the tool that touches the world, and the first byte hyper ever sends anywhere: an artefact, a call, a response object, a projection, a page.
It invokes a `read` Operation against `local` without a Definition, which is the whole of why it exists: §9's argument for a Probe is that the review model dies by volume, an agent authoring a Manifest against an unfamiliar API asking *what does this endpoint actually answer* twenty times. Every one of those questions costing a reviewed Definition is what ends with a repository full of Definitions nobody read.
So it writes nothing. No Record, no Journal entry, no Trigger, no Provenance, no Disposition, no lock, no Store, no credential (ADR-0009). Its instant is its own start, recorded nowhere. It **exits 0 whatever came back** — a 503 as readily as a 200, and a host that answered nothing as readily as either — because a read never halts on what came back (ADR-0050) and a nonzero exit would be hyper deciding that a 503 is bad news. The exit code says whether the command did what it was asked; the rendering says what came back.
Its reach is the one thing it does not escape, and there are two of those. The host it asks for is `local`'s to grant, and one outside that grant is `host-not-granted`, a Refusal at 77 (ADR-0042) — the reach comes from an artefact even where no artefact named the Operation. And a Probe may never invoke an `opaque` Operation, whatever any Target grants, which is a usage error rather than a Refusal because there is no edit that would make it work.
It surfaces the raw response beside the projection, which no credentialled surface does (ADR-0017): a Probe binds `local`, which carries no credential slot, so the wire is visible by construction rather than by a flag.
**`--response` is the second form and it makes no call at all**, reading the Operation's `record:` block against a response object the caller fetched themselves and handed over (ADR-0108). Every rule above bounds a request leaving this machine, so with no request each of them is vacuous rather than skipped, and the Operation may be of any Kind — which is the whole of what it buys: the response to a create is the projection an author most needs to see and the one no calling surface could ever look at, looking being the effect. probeSupplied is where the two forms part, and nothing past that line dials.
func RunProject ¶
RunProject implements `hyper project` — the fifteenth of §9's sixteen, and the first thing in the tool that writes a file into the working tree (§10, §11, issue #177).
**It takes no arguments at all.** It is repo-wide and all-or-nothing: there is no `project <procedure>`, since per-Procedure projection would let two Procedures pin two versions against one Store, so a positional is a usage error at `2`. There is no `--dry-run` either — the diff it writes is the rehearsal, and `git` is where it is read — and the three globals apply with no fourth (§9, ADR-0015).
**What it does, in one act**: one workflow file per Procedure declaring a Cadence, and every file in the namespace no Procedure asks for any more, removed. Generation is whole-file and always overwriting, never merging: a hand-edit to a projected file does not survive, which is correct rather than regrettable, being authority living outside every reviewed artefact (§9, §10).
**It writes nothing where `check` would report anything.** The projection is derived from reviewed artefacts, and deriving from a repository that does not check is deriving from something nobody could review. It runs the same static pass every other reader does, prints `check`'s own problem table and exits `1`; the rule is load-bearing rather than defensive in one case above all, `cadence-malformed` existing precisely so that an expression no grammar admits never reaches an executor's clock (§4, §10, issue #174).
**One code is excluded from that pass and only one**: `projection-stale`, the drift this command is the repair for. Including it would make the command that repairs the drift refuse to run because of the drift, which is a state with no way out of it (§10, issue #179).
**It writes the pin, and nothing else in the tool does.** The version is the binary's own, derived rather than authored, and the digest beside it is the checksum published for that version — so changing the version is *install a binary, run one command, read the diff*, three acts in the open, each leaving something behind (§11, ADR-0020).
**`hyper.yaml` is edited, never regenerated**, and created where the repository holds none. What that costs and why it is not the whole-file rule the workflows are written under is internal/pin's to state; what is this command's is that the edit happens in the same act the workflows do.
**And it writes `AGENTS.md` where the repository holds none**, which is the third path in its namespace and the only one that is not derived from an artefact. The bytes are the orientation internal/mcp already states, at this binary's version, because two orientations disagree the first time either is edited. ADR-0093 refused this and is amended rather than reversed: what moved is that `project` is the documented first act on a new repository, so the cold start the ADR said `project` could not reach is exactly the moment `project` runs (§9, §11, ADR-0095, issue #211).
**It is create-if-absent and never an overwrite**, which is the one place this command departs from whole-file regeneration. `AGENTS.md` is a shared file most repositories already hold for reasons having nothing to do with `hyper`, and ADR-0093 is right that always-overwriting semantics are correct for a generated workflow and wrong for a note addressed to a reader. Where one stands, `hyper` leaves it, and the orientation's own closing paragraph is what covers the gap: the agent offers to add a section, and the human decides.
**And this one stands outside the pin gate**, the only command in §9's tree that does. It is exempt not for being read-only, which §11 refuses as a ground for anything, but for being **the pin's only writer**: a gated `project` on an unpinned repository would Refuse naming itself, and a gated `project` under a newer binary would Refuse naming itself, which makes the upgrade ritual unperformable at step two — a writer gated on what it writes is a bootstrap with no bootstrap. ADR-0001 is untouched: `project` does not proceed under a pin it disagrees with, it replaces the pin and writes the replacement into a tracked file whose diff is the review (§9, §11, ADR-0020).
That sentence is this command's, and the surfaces that assert the exemption point back at it rather than restating it (golden_test.go). **The MCP tool is one of them**: it inherits the exemption by going through the same dispatch rather than declaring one, which is what makes *ergonomics is the whole of the difference between the two* true of the one command that stands outside the gate (§9, internal/mcp's projectTool, issue #203).
func RunProvider ¶
func RunProvider(args []string, to destination, lookupenv func(string) (string, bool), wd, binaryVersion string) int
RunProvider implements `hyper provider <name>` — §9's second discovery question, *which Operation*, and the first command in the tool that takes a name and resolves it. It writes the Manifest's own facts as a header row, emitted first, then one row per Operation the named Provider exposes, and exits 0.
It is `providers` in every respect but its rows and its positional: the same globals, the same gate before the load, the same stream discipline, and the same two properties — it cannot exit 1, reporting facts rather than problems found (ADR-0064), and it reaches nothing: no network, no credential, no Store, no invocation. The Auth scheme it renders is the header the Manifest composes with the credential's position marked, which is a fact about the Manifest and needs no secret to state (§9, ADR-0007).
What is new here is the positional, and the rule it establishes for every command after it: a name resolving to nothing is a usage error, exit 2, carrying no error_code. A Refusal is the artefacts declining an act and a usage error is there being no act to decline — nothing was reviewed, so nothing refused, and the remedy is a different name rather than an artefact edit (ADR-0036, ADR-0060).
It takes no --limit: it names a Manifest rather than ranging over a namespace, so there is no result set for a cap to cut (§9).
func RunProviders ¶
func RunProviders(args []string, to destination, lookupenv func(string) (string, bool), wd, binaryVersion string) int
RunProviders implements `hyper providers` — the first of §9's three discovery questions, *which Provider*, and the one an agent asks before it can write a `provider:` at all. It writes one row per Provider hyper can load, built-in and Extension alike, and exits 0.
It reports facts rather than problems, which fixes two things about it. It cannot exit 1: a Manifest that will not parse contributes no row and its faults are `check`'s to report (ADR-0064), so there is nothing here for a problem count to be. And it reaches nothing: no network, no credential, no Store, no invocation — the whole answer is the repository load, and the load reads the five artefact locations and nothing else.
Its parameters are RunCheck's, for the reason RunCheck's are what they are: everything the command reads from the process arrives as an argument, so the whole of it is exercisable without a subprocess.
func RunRecords ¶
RunRecords implements `hyper records` — the surface whose job is finding a version (issue #166).
`changes` reads a change and this finds the version that change is of. One row per Record: its identity, its ordinal, the Run **and** Step that wrote the version, whether that Run was a rehearsal, whether it is an Observation or an Asset, whether it is a Tombstone, which of its fields carry the presence-only secret marker, and its Provenance (§9).
**The Run and the Step together are the version's identity, and this is the surface that carries them.** Two Steps of one Run writing one identity write two paths (§12), so the Run alone would not name one — which is a distinction only the command whose job is finding a version has to make.
**The ordinal is rendered here and derived nowhere near here.** It is read off the Version the listing answered with, where it was built by the same ordering that derives the Head (store.order) — one derivation over the one listing, and never a second count taken at the surface, which is the whole reason a `records` ordinal and a Comparison's are the same number.
It is each version's position in that ordering, stored nowhere, and never the version's identifier — which is the Run that wrote it (ADR-0011, ADR-0049). It is unstable under Compaction and under a laptop's Observations slotting in beneath a runner's, and that is affordable for exactly one reason: **nothing anywhere accepts an ordinal as input.**
**The ordering is identity**: `(Target, Definition, name)`, each by Unicode code point, the columns read left to right — the one ordering `hyper` has over Record names, reused rather than stated a fourth time (ADR-0044). It is the Store's own (store.Records) rather than one applied here, for the reason `runs`'s is: the reader that answers a listing is the one that orders it.
It is not a Run: it writes nothing, terminates its stream with `result` rather than `outcome`, and exits 0 whatever the Records it listed hold.
func RunReview ¶
RunReview implements `hyper review <artefact>` — §8's Definition review of the artefact named: every line of the working tree's file, verbatim, beneath a header saying what is being read (issue #118), annotated in place by the gutter to its left (issue #120).
What the gutter marks on a Procedure is what a reviewer with the file open in a diff cannot see, because none of it is in the file: each Step's Kind, declared in a Manifest two directories away and never inferable from the Operation's name; the Target it binds; whether a `mutate` carries a Bound; whether the Operation's request is one `hyper` cannot describe; and the envelope check, which quantifies over every Step's `target:` at once. The other four artefacts' rosters are their own and land with them.
It is `check` and `provider` in every other respect: the same globals, the same gate before the load, and the same stream discipline. What it reaches past the artefact is the range: the Store branch this clone holds, and the one git object the range opens at (§8, issue #164). Neither is a fetch — nothing here syncs, and every object read runs with lazy fetching off — so no credential resolves and no network is touched, and a review still answers on a fresh clone of a repository that has never run (§9, ADR-0071).
What is its own is the positional, which takes two forms and where neither is optional. A positional containing `/`, or ending in `.yaml`, is a repository-relative path resolved against the load's own paths; anything else is a name matched against the artefact's own `name:`. Both are mandatory and the reason is symmetric: the built-in Manifest has no file, so a path can never reach it, and `hyper.yaml` declares no name, so a path is the only thing that can (§9, ADR-0060).
Its three exit codes are §9's and all three are reachable here. 0 where the review rendered, however much it had to say. 1 where the artefact under review is found and will not load, which writes `check`'s row for it. 2 where the positional named nothing — the usage error every command that takes a name answers with, no row stream opening and nothing reaching stdout. An artefact that loads and *names* one that is not there is none of those: it renders and exits 0, the fault being `check`'s to report (§9, ADR-0064).
It takes no --limit: nothing on this screen is a result set, an artefact having neither an order nor a cap, so a review that dropped lines would be rendering something other than what is about to be approved (§8, §9).
func RunRun ¶
RunRun implements `hyper run <procedure>` — the tracer bullet, and the first thing in the tool that writes to the record on its own account (issue #136).
The name is the wart §9 already owns: every command in this package is `Run<Command>` and this command is `run`, so a reader looking for `hyper run`'s entry point finds it where they would look for any other's. Nothing else here is named for it.
What it does is decide the occasion and render the answer, and the whole of what happens in between is internal/run's. The split is ADR-0026's, and it is the reason the engine can be exercised without a subprocess: the Trigger, the clock, the mint, the dialer and the streams are read and resolved here, and the engine reaches for none of them.
The order past the gate is §9's *positional, then the Store*: `hyper run typo` is `2` on a repository with no Store at all, because a working-tree name needs nothing further to resolve and the typo is repaired before the Store is missed (§9, ADR-0060). Past that it is §6's fixed order, which internal/run states.
**The positional is a Procedure's name and takes no second form**, unlike `review`'s, which takes a path beside one — the two commands differing rather than the two positionals (ADR-0090, unresolvedProcedure).
func RunRuns ¶
RunRuns implements `hyper runs` — the Journal listed (issue #165).
It is the surface that enumerates the namespace a `<run-id>` resolves against, which is what `show` points a caller at when an id matches nothing (§9). One row per Journal entry, and the row is §9's own seven facts: the Run id, when it started, its Trigger, its outcome, its Procedure, the Targets it bound and the version of `hyper` that performed it.
**The Trigger is on every row**, being the only thing that distinguishes a world that has not changed from one nobody has looked at (§7).
**Two markers ride beside those seven** — the rehearsal and the contest — and neither is an eighth fact: a marker is the qualification the other cells are read under, where the seven are the Run's own account of its work (ADR-0160).
**The ordering is time, and time runs newest-first** (ADR-0065), on `started_at` with the `<run-id>` descending as the tie-break — §7's Head shape, a time key with a name behind it, and a UUIDv7 is total over the tie. The order is the Store's own (store.Entries) rather than one applied here: the reader that answers a listing is the one that orders it, so a Run's Steps and a Journal's entries cannot come to disagree about what *newest* means.
Ordering on the Run's **start** rather than its end is also what gives an open entry a position like any other — an entry with no `outcome.json` still carries a `started_at`, so nothing here needs a rule for one.
**This is the surface that pays the clock skew, and it pays it knowingly.** A Journal entry is a Run, and Runs have nothing to be ordered by except when they happened, so two machines whose clocks disagree by more than the gap between two Runs can list them in an order neither would agree with. `hyper` does not detect it, warn about it, or correct it: every remedy is a clock somebody else owns, and a listing that reordered itself on a guess would be evidence rearranged to look consistent.
It is not a Run: it writes nothing, terminates its stream with `result` rather than `outcome`, and exits 0 whatever outcomes the entries it listed record — the exit code is this invocation's and never any Run's.
func RunShow ¶
RunShow implements `hyper show <run-id>` — one Journal entry read back whole (issue #163).
It is the first command a person can type that reads the record back. The Journal reader in internal/store has been complete since milestone 4 and has been called by the reap, by the run-once evidence walk and by nothing anybody could invoke; this is the surface for it.
**What it renders is what the entry holds and nothing reconstructed.** Its Step rows are the records the entry holds about its Steps, in the Run's own written order — the Step files it wrote, `<nnnn>` by §12's grammar, and, where a reaper closed it, the reading its earliest closing write carries beside them (§7). That is why this command orders nothing (§9).
A Step the Run never reached wrote no file, so it has no row here and no `provenance` row either: that Disposition is read from a silence inside a closed entry (§7), and reading it back would mean loading the Procedure sequence at the Run's own revision — which is the reaper's act, on the four honest absences it costs, and not a thing this surface performs to fill a table.
**The order past the gate is the Store and then the id**, which is the exception to §9's *positional, then the Store*: the Store is the namespace a `<run-id>` resolves against, so an absent branch is reported rather than the id blamed, and an unknown id is reachable only where the branch exists (§9, ADR-0060). A partial id resolves to nothing anywhere (ADR-0047), so a prefix and a typo arrive at one message.
It is not a Run: it writes nothing, terminates its stream with `result` rather than `outcome`, and exits 0 whatever outcome the entry it read records — the exit code is this invocation's and never the Run's.
func RunStore ¶
RunStore implements `hyper store` — the one noun group in §9's tree, and the first command in the tool that writes anything at all (issue #126).
The noun is what makes *this creates a git branch* legible at the point of use: a bare `init` would read as initialising a repository, which is the one thing this command does not do. The group is also where a second verb goes if one is ever earned, and `init` is the whole of it today — the sub-verbs are tree.go's list, so the surface the completion scripts describe and the surface the dispatch accepts are one statement.
It is the first command handed the process whole, which is why its signature carries the value where its six neighbours carry a lookup: every commit `hyper` writes takes both its dates from the clock, so a fixture's branch is reproducible and `git log` on the Store is honest (§7, issue #125, issue #134).
func RunTargets ¶
func RunTargets(args []string, to destination, lookupenv func(string) (string, bool), wd, binaryVersion string) int
RunTargets implements `hyper targets` — the one command in this milestone that asks the repository rather than a Provider, and the only surface anywhere that answers *is the credential in place*. It writes one row per Target declaration and exits 0.
It is `providers` in every respect but its row: the same globals, the same --limit and truncation marker, the same gate before the load, the same stream discipline, and the same two properties — it cannot exit 1, a declaration that will not parse contributing no row and its faults being check's to report (ADR-0064), and it reaches no network, no Store and no invocation.
What it does reach is the environment, which nothing else in this milestone does: presence is computed when the command runs, by asking what the environment did with the variable a credential slot names — it does not hold it, it holds it empty, or it fills it. The value behind a filled name is never read, and nothing on this surface has ever held a secret (§3, §9, ADR-0007, ADR-0145).
func RunVersion ¶
RunVersion implements `hyper version` — one of the three commands that stand outside the tree of sixteen, and the second way to read the version string the pin gate compares (§9, ADR-0020, issue #103). An operator whose `check` just Refused with *this binary is 1.4.0* has been told a version by the process whose identity is in question; this is where it is read on its own.
It is a sibling of RunCheck and takes neither the environment nor a working directory, because neither is reachable from a command that reads no repository: the pin-gate exemption §9 grants it is stated in the signature rather than enforced by a branch inside it. facts is passed in for the same reason golden files exist at all — a page assembled from the running build changes with every commit made to the tree.
It reaches no network on any path and never asks whether a newer version exists (ADR-0016, ADR-0019).
func Shells ¶
func Shells() []string
Shells is the set `hyper completions` writes a script for, alphabetically.
func StoreSubVerbs ¶
func StoreSubVerbs() []string
StoreSubVerbs is `store`'s sub-verbs, the tree's only nesting.
func Streams ¶
Streams is that value, assembled from the two writers a process hands the tool. Main assembles its own out of the argv's own streams and threads it down; this is the door for a caller outside the package that drives one command rather than the dispatch, which is what every case reaching an entry point directly does.
**It answers the interface and not the struct**, so what a caller outside can do with a destination is exactly what a command can do with one: hand it an answer, hand it a Refusal, narrate. That asymmetry — an exported constructor over an unexported type — is the point rather than an oversight: a destination is `hyper`'s to implement, both the one that exists and the one the milestone after this adds, and neither is a shape a caller supplies. What is exported is the ability to obtain the CLI's, which is what an entry point driven directly needs and all it needs.
The form is not set here — the flag that names one is read by the shared parser, which is the only reader of it there is (flags.go).
Types ¶
type Notify ¶
Notify is how the signals are watched: the signals to watch for, and the channel and the release the watch is held by. It is a named type for Dial's and Exec's reason — one signature, spelled where the thing that uses it is stated rather than twice — and it is `os/signal`'s own shape, so the binary wires that package in one expression and nothing else in the tree imports it.
type Process ¶
type Process struct {
// LookupEnv reads one environment variable, and answers whether it was
// set at all rather than only what it said. Both halves are load-bearing:
// HYPER_REPO_DIR is the second of §9's three configuration layers, and a
// credential slot is *present* or *absent* by whether its variable is
// set — a variable set to the empty string is present and says so (§9,
// §5, issue #112).
LookupEnv func(name string) (string, bool)
// Environ is the whole environment, and it is read for exactly one
// thing: a `shell` Operation's child inherits the invoking environment
// with every credential-slot variable in the repository removed (§3,
// §11, issue #142).
//
// It is a second read of one subject rather than a widening of the
// first, and the two cannot be folded. LookupEnv answers *what does this
// name hold*, which is the whole of what a credential slot and
// HYPER_REPO_DIR ask; composing a child's environment is a subtraction,
// and a set of names nothing enumerates is not a set anything can
// subtract from. Nothing else in the tree reads it: the git subprocesses
// internal/store runs compose their own deliberate inheritance, which is
// the record's transport rather than anything an artefact named (§7,
// ADR-0006).
Environ func() []string
// Getwd is where the invocation is standing. The dispatch calls it on the
// repository commands' arm and hands the answer down, so no command
// behind it has a reason to call it again — the third configuration layer
// is the git root above the working directory, and it is resolved once,
// where a command needs one. That the exempt arm calls it never is the
// dispatch's own shape and is asserted there: a working directory that
// cannot be read does not stop `hyper version` (§9, ADR-0020, issue
// #103).
Getwd func() (string, error)
// User is who is running hyper, and Hostname is the machine they are
// running it on. Both are read for one value each in the whole tool: a
// Journal entry's Trigger carries `actor` on both executors and `host`
// on `local`, which is what §8's header renders `igor@thinkpad` from
// (§7, §12).
//
// They are two reads rather than one because they come from two places
// — the passwd database and the kernel — and either can answer while
// the other does not.
//
// They are threaded rather than read because everything in this value
// is, and because a fact that lands in the record must be a fact a
// fixture can supply: an entry whose `host` came from the machine the
// suite ran on is a `store.golden` nobody can check in. Each answers an
// error the way its standard-library reading does, and a Run that
// cannot read the machine writes no `host` at all — the ordinary
// absence rule, and better than a constant hyper invented for a machine
// it knows nothing about.
User func() (string, error)
Hostname func() (string, error)
// Now is the clock. Every commit `hyper` writes takes both its dates from
// it, so a branch a fixture builds is reproducible and `git log` on the
// Store is honest; retention is an age and is measured against it; and a
// Run's start instant is read once and every date the Run writes is that
// instant (§6, §7, issue #125, issue #131).
Now func() time.Time
// Mint mints a Run id at the instant it is handed.
//
// store.MintRunID reads crypto/rand, which is a read of the process
// exactly as the clock is, and this is the member that is easiest to
// miss: 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 by
// whichever function happened to want one makes every golden of a Run
// unassertable. The fix is to thread the read rather than to normalise
// the id out of the goldens — §8 states that a Run id renders **whole**
// (ADR-0047), and a corpus asserting `<run-id>` could not check the one
// rendering rule that surface has.
Mint func(now time.Time) store.RunID
// Dial is how a connection to a host is made, and it is what the `http`
// Capability's client dials through. Threading it is what lets a golden
// case exercise a real handshake, a real status line and a real parse
// against a server standing in the test process, with the name
// resolution the only thing a fixture supplies — the response object is
// never written down by a test (§5, issue #133).
//
// It answers a connection that is already past its TLS handshake, and
// that is a fact about hyper rather than about this signature: the
// scheme is `https` and there is no second one (ADR-0082), so every
// connection hyper makes is a TLS connection, there is no plaintext
// dialer to supply, and the certificate the peer presented is a real one
// off a real handshake — which is what tls.days_left is read from (§12).
// internal/capability wires it as http.Transport's DialTLSContext and
// holds no TLS configuration of its own, and names the type: one
// signature, spelled where the Capability that dials through it is
// stated rather than twice.
Dial capability.Dial
// Exec is how a child process is started: it answers the child that argv
// names, ready to run, carrying the two launch decisions that belong to
// the process rather than to a Capability — the child starts in its own
// process group, and cancelling ctx kills that whole group with SIGKILL
// and no grace period, which is what a Manifest's deadline is (§5, §6).
//
// argv is a list with a literal head and nothing stands between it and
// the process: there is no shell here, so a pipe, a redirection, a glob
// and an `&&` are not writable (ADR-0051). What the caller sets on the
// answer is everything about the child that the Manifest decides — the
// directory, the environment, the streams — and never a process
// attribute, which is what keeps the process group decided in one place.
//
// It is a Capability's child and only that. The git subprocesses
// internal/store runs are the record's transport rather than anything an
// artefact named: their argv is compiled in, their environment is that
// package's own deliberate inheritance, and a deadline that SIGKILLed a
// push mid-write would be a bound nothing declared. They do not come
// through here, and a Manifest cannot reach them (§7, ADR-0006).
//
// It names the type for Dial's own reason: one signature, spelled where
// the Capability that starts a child through it is stated rather than
// twice. Child below is the value the binary wires into it (issue #142).
Exec capability.Exec
// Notify is the process's signals, watched: it is handed the signals to
// watch and answers the channel they arrive on and the function that
// stops the watch (§6, ADR-0015, issue #145).
//
// It is the tenth read and it is threaded for the reason the other nine
// are: a Run stopped by an interrupt writes a Journal entry and a
// terminal line, and a case that could not deliver one could assert
// neither. What it stands for is the whole of `os/signal` — nothing
// behind the dispatch imports that package — so a case drives the same
// drain the terminal does, with the delivery its own.
//
// **Stopping the watch is what makes a second interrupt kill the
// process.** Go's signal package replaces the default disposition while
// a channel is registered and restores it when the last one goes, so the
// Run that has already drained on the first signal releases the handler
// and the next one lands on the kernel's own answer — which is §6's open
// entry, and the reason there is no second drain to write (§7).
//
// It may be nil, and a Run under a nil Notify is one nobody can
// interrupt: nothing is installed, nothing is caught, and the Run
// performs to its end. That is what every command but `run` is handed,
// there being no Run under them to stop.
Notify Notify
}
Process is everything `hyper` reads from the process it is running in, as one value.
It is issue #100's property at the grain a milestone of eight reads needs: everything a command reads from the process is a parameter it is handed rather than a package it reaches for, which is what makes the whole dispatch exercisable without a subprocess. What travels beside this value rather than in it is the argv, which is what the dispatch decides on; the destination, which a command writes to rather than reads and which is assembled out of the two streams the process handed in (destination.go); and version.Facts, which the build stamped rather than the process holds.
Six loose parameters threaded through repositoryCommand is where that property starts costing more than it buys, so the reads travel as one value: what a command may read is a type a reader opens rather than a parameter list they count, and the milestone that adds a seventh read changes no signature at all (issue #134). The seventh and the eighth landed with `run`, which is the claim made good: User and Hostname were added below and no signature in the tree moved. The ninth landed with the `shell` Capability and moved none either (issue #142), and the tenth landed with the signals a Run drains on (issue #145).
It is one trade and worth naming. A command handed the whole value says *I may read the process* where its signature used to say *I read the clock and nothing else*, and for the two commands that take it — `store` and `compact` — the finer statement is gone. main.go's environmentOnly is what keeps it everywhere it can still be made: a command that reads only the environment takes only a lookup, and five of §9's sixteen still say so by their shape. `review` was the sixth until it opened a range and started reading the clock the age beside its gloss is measured against (issue #164), which is the trade working as stated — the signature moved because what the command reads did.
Every member is a function rather than a resolved value, and for the reason the working directory already was one: a read a command never makes is a read that never happens. `version` and `completions` resolve no working directory and call no gate, and that exemption is a path not taken rather than a value quietly computed for nobody (§9, ADR-0020) — whatever a command does not read is still visible in what it is handed.
Source Files
¶
- answer.go
- authority.go
- cadence_gloss.go
- changes.go
- changes_code.go
- check.go
- child.go
- compact.go
- completions.go
- destination.go
- exit.go
- flags.go
- gate.go
- inspect.go
- install.go
- main.go
- mcp.go
- operation.go
- probe.go
- process.go
- project.go
- provenance.go
- provider.go
- providers.go
- records.go
- refusal.go
- review.go
- review_changes.go
- review_flags.go
- review_marks.go
- review_range.go
- run.go
- runs.go
- show.go
- signals.go
- store.go
- targets.go
- tree.go
- trigger.go
- usage.go
- version.go