compare

package
v0.0.3-alpha Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package compare is the Comparison (§8, issue #167): one Run rendered against the Run before it, as an ordered list of rows.

**It is pure.** It opens no file, starts no subprocess and reads no clock. Two Journal entries, the Store reads those entries need and the artefacts at both revisions are handed in; what comes back is the row list, which `internal/render` writes as the page and as the `--json` stream (ADR-0026). The git reads are `internal/revision`'s and the Store reads are `internal/store`'s, made by the caller.

The reason it is a package and not a file under `internal/cli` is size and not taste. What lives here is the window rule, the endpoint Head-at-instant, identity-set eligibility, six change names with one precedence rule between them, the ordinal in four forms, nine code-fact classes and `TOTALS`' three ordered forms — a derivation `internal/cli/changes.go` should be able to call rather than contain, exactly as it calls `internal/run` and `internal/store`.

What stands here is the window and the header (#167), the two Record tables beneath them (#170), and `THE CODE MOVED` with `TOTALS`' last segment (#171).

Index

Constants

View Source
const (
	SubjectProcedure  = "procedure"
	SubjectDefinition = "definition"
	SubjectTarget     = "target"
	SubjectManifest   = "manifest"
	SubjectRepository = "repository"
)

The five subject kinds §8's `SUBJECT` column qualifies a name with. **A header reading `DEFINITION` misdescribes every row whose fact belongs to something else**, and a bare name is ambiguous across kinds; §12 fixes each `kind:` to one directory, so the kind and the name together are a whole path in one short cell.

They are the page's words rather than §12's `kind:` values — `target` where the wire says `target-declaration`, `manifest` where it says `provider` — which is the same two-spellings-of-one-fact the review's own marker column header already is (§8, internal/cli's artefactKinds).

View Source
const (
	FactProcedureRevision  = "procedure revision"
	FactDefinitionRevision = "definition revision"
	FactManifestDigest     = "manifest digest"
	FactOriginDigest       = "origin digest"
	FactRepositoryRevision = "repository revision"
	FactHyperVersion       = "hyper version"
)

The six facts `the digests` names, one per Provenance member (§7, §12).

**The class is stated intensionally on purpose**: a member joining the field set brings a row here without §12's enumeration moving, which is how the Procedure revision arrived (ADR-0048) and why there are still nine. It is the one class with no key to name, being Run-recorded with no line in any artefact, so its rows name their member.

View Source
const (
	// FactOtherLines is the catch-all's own name, and it is the name on the
	// wire in both of that row's forms: the absence carries
	// `baseline_absent` beside the `command` it keeps and drops `count`,
	// **rather than changing `fact`** (§8).
	FactOtherLines = "other lines changed"
	// NotInClone is the one §12 absence this table can carry, and the same
	// name the review's header renders: the Store answered, the window is
	// the right one, and what failed is the clone not holding an object
	// somebody else's Run recorded (ADR-0071).
	NotInClone = "not-in-clone"
)

Variables

This section is empty.

Functions

func CodeMovedPhrase

func CodeMovedPhrase(rows []CodeRow) string

CodeMovedPhrase is `TOTALS`' last segment, and **its three forms are tested in order**: any classed row rendered → *the code moved*; otherwise the absence line rendered → *the code could not be fully read*; otherwise → *the code did not move*.

**The order is what makes the line honest rather than merely careful.** A surviving classed row is positive proof where the absence line is proof of nothing either way, so a window in which a Bound moved *and* a Target declaration could not be read is reported by the fact — the line above having already named what went uncounted. What the ordering removes is the one reading this table may never produce: the negative asserted over bytes nobody read (§8).

It is a phrase and not a count: summing a classed fact, a repository revision and a line count into one integer is three incommensurable things under one head.

func CodeRows

func CodeRows(window Window, code Code) []render.Row

CodeRows is `THE CODE MOVED`: the classed rows in `(SUBJECT, FACT)` order and the catch-all last of all.

**Rows sort by `(SUBJECT, FACT)` on Unicode code point, with the `—` subject after every named one.** `—` means *this fact belongs to no artefact you can open*, so it sorts away from the rows that do, and §12 already fixes the catch-all as terminating the table (§8).

A window with no baseline draws nothing at all. Its subject Run is the first Run of its Procedure, so there is no earlier revision for code to have moved from, no pair of revisions for `git diff` to name, and nothing for a count to be a count of.

func Eligible

func Eligible(window Window) []store.Identity

Eligible is the identities a window may draw a row for, in `(Target, Definition, name)` order.

**They come from the identity sets and never from the Store** (ADR-0058). A row exists for an identity some Step of the subject Run or of the baseline Run concluded about (§7), which is what keeps another Procedure's work out of this Procedure's tables — the same evidence that already decides *vanished*, *appeared* and *nothing moved* deciding eligibility outright.

The endpoints are then read without asking whose Run wrote them: a Record this Step concluded about, which another Procedure moved in between, renders `changed` and the gap shows in `ORDINAL`. Reading `run_id` to name a row would make this surface report authorship across Procedures, which is the join the window rule exists to prevent.

A Step's Target and Definition are what turn its set's names into identities: a set holds the names a Step concluded about, and the Step it sits in is what says which series each of them belongs to (§7).

func Endpoint

func Endpoint(side Side, series store.Series) (store.Version, bool)

Endpoint is §7's Head derivation with one side's instant as a cutoff: the version a reader would have called the Head had they looked then.

It is a prefix of the same ordering the Head comes off — `written_at`, ties broken by the file name — so it is that derivation read short rather than a second one, and a version written after the instant is not consulted at all.

A side that is not there holds no version. That is the baseline of a first Run, where every Asset the subject holds is `created` and every Observation `appeared`, and it is the answer this returns rather than a caller's special case.

func Rows

func Rows(window Window, records []Record, code Code) []render.Row

Rows answers the ordered rows of one window: the `window` row, then the rows of `YOU DID THIS`, then the rows of `THE WORLD MOVED`, then the rows of `THE CODE MOVED` and the catch-all that terminates it.

records is what the caller read for the identities Eligible named — the two endpoints of each, and what they projected. Nothing here opens a file: which identities are eligible and which version stands at each end are this package's (Eligible, Endpoint), and the bytes behind them are the Store's.

**The order is the page's and it is a contract rather than a consequence** (§8, ADR-0026): a row goes out on its own line, there is no cursor behind the stream, and a consumer cannot re-sort what it has already printed.

code is what the caller read for the third table: the reviewed artefacts at both revisions and what git says moved between them (code.go). `TOTALS` gets no row here or anywhere — §8's stream carries the rows of the tables and the `window` row above them and no `totals` object, that line being those rows counted rather than a fact of its own (internal/cli's totalsLine).

Types

type Change

type Change string

Change is the name a row carries, and §8 closes the set at five words over two tables: Assets render `created`, `changed` and `destroyed`, Observations `appeared`, `changed` and `vanished`, exclusive within each table.

The word is the same in both tables where it is the same fact, which is why there are five and not six: `changed` is *the endpoints differ* whichever actor the table names.

const (
	// ChangeCreated is an Asset the baseline end held no version of.
	ChangeCreated Change = "created"
	// ChangeChanged is two endpoints that differ. It is the one name read
	// from the Records alone in both tables.
	ChangeChanged Change = "changed"
	// ChangeDestroyed is a subject end holding a Tombstone the baseline end
	// did not, a Tombstone being a marker inside the Asset table rather
	// than a class of its own (§7, ADR-0033).
	ChangeDestroyed Change = "destroyed"
	// ChangeAppeared is an identity the subject Run concluded about and the
	// baseline Run did not. It is Disposition-derived, which is what makes
	// it beat ChangeChanged wherever both fire.
	ChangeAppeared Change = "appeared"
	// ChangeVanished is an identity the baseline Run concluded about and
	// the subject Run did not: a thing that stopped being there, with the
	// baseline end's fields beside it because the Store holds no version
	// minted for a disappearance.
	ChangeVanished Change = "vanished"
)

type ChangeRow

type ChangeRow struct {
	Type        string   `json:"type"`
	Change      string   `json:"change"`
	Target      string   `json:"target"`
	Definition  string   `json:"definition"`
	Name        string   `json:"name"`
	FromOrdinal *int     `json:"from_ordinal,omitempty"`
	ToOrdinal   *int     `json:"to_ordinal,omitempty"`
	ConfirmedAt string   `json:"confirmed_at,omitempty"`
	Fields      FieldSet `json:"fields"`
}

ChangeRow is one row of `YOU DID THIS` or of `THE WORLD MOVED`, and its `type` is which of the two.

**One type carries both tables** because §8 gives them one derivation: they differ in their three change names and in `confirmed_at`, and a second declaration would be the same members in the same order written twice, free to drift in either. The members are §8's own, in §8's order.

**`from_ordinal` and `to_ordinal` are absent exactly where the column renders `–`** — §7's absence rule saying *nothing to name on this side* by writing no key, which is what a `vanished` row carries in place of a subject ordinal.

**`confirmed_at` stands on a `destroyed` row and nowhere else.** It is the Tombstone's own `written_at`, which §7 states is when destruction was confirmed, and it is the instant the page renders as `† confirmed 11:02`.

**`fields` carries every value whole**, including the ones the page rendered `changed` (ADR-0059): the elision is that column's geometry and never a fact either surface states. It is written always, the empty mapping included, because an empty one is what says *`hyper` destroyed this and never observed what it was* on the one row that can hold it (§7, ADR-0033).

func (ChangeRow) Cells

func (r ChangeRow) Cells() []string

Cells is the row's line on its table, in that table's column order: `CHANGE`, `TARGET`, `DEFINITION`, `RECORD`, `ORDINAL`, `FIELDS`.

Every cell is derived from the row's own members, so the page cannot render a fact the stream did not carry (ADR-0026). The two that render differently here are the ordinal, whose absent side is a `–` where the wire writes no key, and `FIELDS`, whose elision is this column's geometry (ADR-0059).

type CloserRow

type CloserRow struct {
	Run     string `json:"run"`
	Outcome string `json:"outcome"`
	Step    int    `json:"step,omitempty"`
	Ended   string `json:"ended"`
}

CloserRow is one closing write on a side of the window: another Run's inference that this entry's Run had died, and the whole of what the contest line beneath the header renders.

The Run is the file's name and not one of its members — a closing write carries none naming its author, its path being that member (§7, ADR-0076) — and `outcome` is what §7 fixes every closing write as, written out rather than left implied because the page states it, and a fact the page states and the wire does not is the two surfaces disagreeing (ADR-0026).

type Code

type Code struct {
	Baseline CodeSide
	Subject  CodeSide
	// Count is git's own count of the lines that moved over the reviewed
	// five, before the classed rows subtract their own: added and removed
	// as git counts them, a modified line being two (§12).
	Count int
}

Code is what the caller read for `THE CODE MOVED`: the reviewed artefacts at each end of the window, and what git says moved between the two revisions.

It is handed in whole, this package opening no file and starting no subprocess: the git reads are `internal/revision`'s and the parse is `internal/repository`'s, made by `internal/cli` (compare.go).

A window with no baseline carries none of it. There is no earlier revision for code to have moved from, so the table renders no row and no catch-all — the count being a count between two revisions, and a command naming one reproducing nothing.

type CodeArtefact

type CodeArtefact struct {
	// Kind is one of the five subject words above.
	Kind string
	// Name is the name the artefact declares for itself, and the path for
	// the one artefact that declares none.
	Name string
	// Path is where the file sits, relative to the repository root — the
	// key the moved lines are held under, and "" for the one Manifest with
	// no file in the repository (§11, ADR-0039).
	Path  string
	Facts []artefact.ChangeFact
}

CodeArtefact is one artefact at one revision: the subject it is on the page, where it sits in the repository, and the facts `internal/artefact` read off its lines.

type CodeRow

type CodeRow struct {
	SubjectKind string
	Subject     string
	Fact        string
	From, To    CodeValue
	// Count is the catch-all's own, and a pointer because `0` is a count
	// this row must be able to state: a window in which every moved line is
	// reported by a classed row above reads `0 other lines changed`.
	Count *int
	// BaselineAbsent is `not-in-clone` on the catch-all's replacement form
	// and "" everywhere else. It stands **in place of** `count`, the count
	// being the part that needed the bytes (§8, §12).
	BaselineAbsent string
	// Command is `git diff <rev> <rev>`, abbreviated as the page draws it —
	// the one string on this wire that keeps the page's abbreviation, being
	// a command a reader runs rather than an id the row reports, and one
	// git resolves short (§8). It is "" where either side recorded
	// `repo_dirty`.
	Command string
}

CodeRow is one row of `THE CODE MOVED`, and the catch-all that terminates the table is one of these too.

**One type carries both** because §12 states the catch-all as a row of this table rather than as a line beneath it, and because a consumer filtering `select(.type=="code")` gets the enumeration and the count that makes it sum to the whole. The two are told apart by `fact`, which is the name §8 fixes and which the absence form keeps.

**`subject_kind` and `subject` stand where the fact has an artefact subject and neither stands where it does not** — the pair being the one cell the table renders `—` in, which `repo_revision` alone earns: it belongs to no artefact a reader can open (§8).

func CodeRowsIn

func CodeRowsIn(rows []render.Row) []CodeRow

CodeRowsIn is the code rows read back off a row list, which is how a page reads them: the block is written from the rows, so the stream and the page cannot carry different facts (ADR-0026).

func (CodeRow) CatchAll

func (r CodeRow) CatchAll() bool

CatchAll reports whether this is the row that terminates the table: the count, or the line that stands where the bytes could not be read.

func (CodeRow) Cells

func (r CodeRow) Cells() []string

Cells is the row's line under `SUBJECT`, `FACT`, `FROM`, `TO`.

The catch-all has none: it is a stated line beneath the table rather than a row of it, on the shape §8 renders and the shape the review's own `3 definitions did not load` line already has. What it says is Line below.

func (CodeRow) Line

func (r CodeRow) Line() string

Line is the catch-all's own line, and "" on every classed row.

**Two suppressions, and they stack.** Where either side recorded `repo_dirty` the command is suppressed and the row renders `N other lines changed` alone, `git diff <rev> <rev>` not reproducing what moved. Where the clone does not contain a revision the window names, the count is replaced by the line naming what could not be read — which keeps the command, the reader of a job summary rarely being in the clone that came up short (§8).

func (CodeRow) MarshalJSON

func (r CodeRow) MarshalJSON() ([]byte, error)

MarshalJSON writes the row in §8's own key order, and writes a member only where the row carries one.

It is assembled rather than tagged because three of the members are values rather than strings — a `from` may be a number, an array or a mapping, and the Cadence gloss's parts ride beside it — and because the absence rule is stated over a **key**: a side with nothing writes no key, exactly as `from_ordinal` does two row types up (§7, §8).

The canonical encoding indents and breaks lines and this wire is compact, so the assembled object goes through one compaction: a value read off the Store and a value read off an artefact arrive in one notation (FieldSet.MarshalJSON, one file over).

type CodeSide

type CodeSide struct {
	// Revision is the `repo_revision` the entry recorded — the commit the
	// catch-all's `git diff` names and the tree the artefacts were read at.
	Revision string
	// InClone says this clone holds that revision. **A revision it does not
	// hold is an ordinary fact about the clone** rather than the world
	// resisting: a Run recorded on a runner names a commit a laptop may
	// never have fetched (ADR-0071).
	InClone bool
	// Dirty is the `repo_dirty` this entry recorded. The bytes that Run
	// read are nowhere in git, which is what suppresses the catch-all's
	// command — a `git diff <rev> <rev>` that does not reproduce what moved
	// is worse than no command at all (§7, §8).
	Dirty bool
	// Artefacts are the reviewed artefacts at that revision, each with the
	// facts its own lines carry.
	Artefacts []CodeArtefact
	// Moved is which line of which path git says moved, read at this
	// revision. It is what a classed row subtracts its own lines out of.
	Moved map[string]map[int]bool
}

CodeSide is one end of the window's code: the revision that entry recorded, whether this clone holds it, and the artefacts it held.

type CodeValue

type CodeValue struct {
	// Written says this side states a value at all.
	Written bool
	// Shape decides both what the cell renders and what makes two sides
	// differ. It follows the value at the row and never the class above it:
	// the Target set class alone carries a set for a Procedure's envelope
	// and a scalar for the `target:` a Step binds (§8).
	Shape artefact.FactShape
	// Members are a set's members sorted by code point, a `values:`
	// selector's members **as authored** — its order is the fact — and a
	// predicate selector's conjuncts one per line, sorted by code point on
	// the rendered line (§6, §8).
	Members []string
	// Text is a scalar as written, and a selector's form: `values`, `assets`
	// or `observations`. A cell dropping the form could not tell an `assets`
	// selector from an `observations` one, which is the difference between
	// ranging over what `hyper` built and over what it read (§5, §8).
	Text string
	// Phrase and RateText are the Cadence gloss, and they stand on no other
	// row: cron is write-only for humans and agents alike wherever it is
	// read, and this row is the one place the whole tool renders a Cadence
	// *moving* (§10, ADR-0005, ADR-0063).
	Phrase, RateText string
	// CadenceFacts are §10's two facts about how the executor will treat
	// this side's declaration, which stand beside the gloss wherever it
	// renders and stack under the rate in a cell (internal/cadence). The
	// name is qualified because a CodeArtefact's `Facts` are §12's code
	// facts, and one file naming two things `Facts` is one file with two
	// vocabularies in it.
	//
	// They are this side's own rather than the row's, which is what makes
	// the hour-boundary one legible: it is a reading of one expression's
	// minute field, so a Cadence moving off the hour renders it in `FROM`
	// and not in `TO`. They reach no member of the wire — the row is closed
	// at the gloss's three parts and both are derived from `cadence` and
	// `phrase`, which it already carries (§8, §9, §10).
	CadenceFacts []string
	// Rate is the number the page rounded into RateText, carried for the
	// wire: the parts and never the composed cell, which is the `artefact`
	// row's rule one command over (§8, §10).
	Rate *float64
	// Wire is the value in the artefact's own parsed shape, and nil where
	// the page renders `–` — the key absent rather than null, which is
	// `from_ordinal`'s rule two row types up (§7, §8).
	Wire json.RawMessage
}

CodeValue is one side of a code row: what the page renders and what the wire carries, which are one value in two notations rather than two facts.

**A side with nothing renders `–`**, including where the format states a value by omission — an absent `bound:`, an absent `over:`, an absent `cadence:`. Naming what an absence means is a claim and not a value, and `FLAGS` one surface over is the one editorial place in the tool (§8).

type End

type End struct {
	Held    bool
	Version store.Version
	Fields  store.Mapping
}

End is one end of the window for one Record: the version a reader would have called the Head had they looked at that end's instant, and what that version projected.

Held is what says the end has a version at all, and it is a member rather than a nil check because both of the states it distinguishes are ordinary: a `created` row's baseline end holds none, and so does the baseline end of the series a Tombstone opened.

Fields is the version's projected content, read by the caller. It is nil on an end holding no version, and nil on a Tombstone opening the series it ends — the absence §7 reads as *`hyper` destroyed this and never observed what it was* (ADR-0033).

type FieldChange

type FieldChange struct {
	Path string
	From store.Value
	To   store.Value
}

FieldChange is one field of a row's `FIELDS`: the path, and what stood at each end of the window.

A side that carried no such field holds nil, which is the ordinary absence: a field whose path resolved to nothing is not in the version at all, and the field not being written is what carries that (§6, §7).

type FieldSet

type FieldSet struct {
	Paired bool
	Fields []FieldChange
}

FieldSet is a row's `FIELDS` as both surfaces read it: the fields it renders, **sorted by Unicode code point**, and which of the two shapes the wire writes them in.

The order is the Store's own canonical encoding read out rather than a second ordering, and there is **no cap on how many render**: a wide cell is a Manifest author's projection choice rendered honestly, and capping it is `hyper` guessing at a number in the way ADR-0045 declined (§8).

Paired is the two-sided shape — `{"path":[from,to]}` — and is what a `changed` row carries. A one-sided row carries `{"path":value}`, there being no other side and the cell describing what the thing is rather than how it differs.

func (FieldSet) MarshalJSON

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

MarshalJSON writes the fields as one object, in the order they are held.

It is assembled rather than handed to a map because the order is the fact: two renderings of one window are byte-identical and diffable, which a member whose order came out of a Go map iteration would not be (§8).

**Every value goes out whole**, the ones the page rendered `changed` included: the elision is the column's geometry and never a fact either surface states (ADR-0059). A value the Store holds nested goes out as the artefact's own parsed shape rather than as anything the page composed.

On a `changed` row a side that carried no such field goes out as `null`. It is the one place on this wire an absence is written rather than left out: §7's rule is stated over a **key**, and a two-element array has no key to omit — the array being §8's own shape for the pair. `null` there is the same *nothing on this side* the page renders `–` for.

type Record

type Record struct {
	Identity store.Identity
	Baseline End
	Subject  End
}

Record is one eligible identity with both ends of the window read for it.

It is the caller's read and this package's derivation: which identities are eligible is Eligible below, which version stands at each end is Endpoint, and the bytes behind them are `internal/store`'s.

type Selection

type Selection struct {
	// Procedure is the Procedure named positionally, and "" is the
	// whole-Store mode: naming nothing compares across every Procedure at
	// once, which is why the Procedure is positional on `changes` and a
	// parameter on `runs` — it decides which rendering you get rather than
	// filtering the rows of one (§9).
	Procedure string
	// Since and SinceNamed are `--since`: take the last Run before that
	// instant and fold everything after it into one rendering. The second
	// member says a window was asked for at all, the zero instant being a
	// point in the year 1 rather than *no bound* (internal/cli's own
	// reading of the same flag).
	Since      time.Time
	SinceNamed bool
}

Selection is what the caller named: which Procedure, and the one of §9's three ways of naming a window that has a rule behind it.

**The other two are not here, and for one reason.** `--between` names two Runs directly and `--subject` names one, so each resolves an id against the Journal and builds a Window outright — `--subject` with Preceding below for the end it left derived. What this type carries is the selection that is a *rule* rather than a name: the newest nameable Run, and `--since` moving the baseline behind it (§8, §9, ADR-0115).

type Side

type Side struct {
	Present bool
	Entry   store.Entry
	// Steps is the entry's Step records, in the Run's own written order —
	// the Step files it wrote and, where a reaper closed it, the reading
	// its earliest closing write carries beside them (§7). It is a field
	// rather than a constructor argument because Select answers the two
	// entries a window names before the caller has read anything about
	// their Steps: the selection costs one listing, and the Step files of
	// two entries per Procedure are read once those two are known.
	Steps []store.StepFile
}

Side is one end of a window: the Journal entry that end is, and the records that entry holds about its Steps.

The Step records travel with the entry because the endpoint instant needs them — a reaped entry's is the last Step file's `ended_at` (Instant below) — and because the tables above will read the identity sets off the same files. They are the caller's read: `store.Store.Dispositions` answers them, and this package is handed the answer rather than the Store.

Present is what says the end exists at all. A Comparison always has a subject; a baseline is what the first Run of a Procedure has none of, and the header states that absence as a named state rather than rendering an empty line (§8).

func Preceding

func Preceding(entries []store.Entry, subject store.Entry) Side

Preceding is the baseline behind a subject a caller named by id: the newest nameable Run of the **same** Procedure to have started before it, and an absent Side where there is none.

It is the half of Select's rule that is a rule at all. `--subject` names one end directly, the way `--between` names two, so what it leaves to be derived is the other end — and deriving it here rather than in the command is what keeps *which Run is a baseline* one answer: the same filter, the same Procedure window and the same ordering, whether the subject was chosen by rule or typed.

**A rehearsal and an open entry are passed over here as they are there**, and that is the whole of what `--subject` does not lift. §7 names four readers that filter a rehearsal's entry out and this is one of them; what §8 now distinguishes is the *subject*, which a caller may name, from the *baseline*, which only a rule ever chooses — this one, or Select's. A rehearsal named as a subject therefore never becomes a baseline: not its own window's, and not one behind a later subject either.

The subject is excluded by the comparison rather than by identity: `newest` is total over the two keys §9 orders Runs on, so an entry that is the subject compares equal to it and is dropped along with everything after it. A window whose two ends were one Run would render a Comparison of a Run against itself.

func (Side) Instant

func (s Side) Instant() time.Time

Instant is this side of the window: the entry's **own last** instant.

It is `outcome.json`'s `ended_at` where the Run wrote one, and the last Step file's `ended_at` where the entry's only account is a `closed-by/` file — the instant §7 names as *when the Run went quiet*, read as a timestamp and never as a verdict.

**It is never the closing write's**, which is the whole reason this is not `store.Entry.Ended`. That instant is the *closing* Run's on the closing Run's clock, so a Run reaped a week later would sweep every intervening Run's versions into its side of the window — the same cross-entry reading §7 forbids a duration for, applied to an endpoint (§8).

A contested entry takes no special reading: it holds an `outcome.json` its own Run wrote, so its side is that `ended_at` like any other Run's, and the `closed-by/` file beside it is never an endpoint.

An entry with neither — an open one — has no instant, and Select never puts one on either side of a window (StandingOf below).

func (Side) Reaped

func (s Side) Reaped() bool

Reaped answers whether this side's only account of how it ended is a closing write, which is what says no duration derives for it (§7, store.Entry.Duration).

type SideRow

type SideRow struct {
	Run string `json:"run"`
	// Trigger is the composed string and never the mapping, which is §8's
	// own `window` row and the same reading `runs` takes of the same fact:
	// a clock or a person, which is the whole of what §7 says a Trigger
	// distinguishes. `show` is the surface that carries the four members an
	// executor writes, its job being one entry read whole.
	Trigger string `json:"trigger"`
	Started string `json:"started"`
	// DryRun is whether that Run was a rehearsal, and it is written
	// always — the bare `false` included. It is §7's one exception to the
	// absence rule, carried onto the surface that names two Runs for the
	// reason it is carried onto `records`' rows and `show`'s header: a
	// reader that takes its absence for `false` gets a permanent wrong
	// answer, and `--subject` is what puts a rehearsal on this wire at all
	// (§7, §8, ADR-0114, ADR-0115).
	//
	// A baseline's is `false` on every window there can be — Select and
	// Preceding both pass a rehearsal over when choosing one, and
	// `--between` refuses one in that position — and it is written there
	// anyway, an exception that held on one member of a pair and not the
	// other being a shape a consumer has to learn twice.
	DryRun            bool        `json:"dry_run"`
	Outcome           string      `json:"outcome"`
	Ended             string      `json:"ended,omitempty"`
	ProcedureRevision string      `json:"procedure_revision"`
	RepoDirty         bool        `json:"repo_dirty,omitempty"`
	ClosedBy          []CloserRow `json:"closed_by,omitempty"`
}

SideRow is one end of the window as the wire carries it: the six facts §8 says the header names each Run with, and the two markers that qualify them.

**`ended` stands where the page renders a duration.** §7 is precise that no duration is stored anywhere — a stored duration is a second representation of what the timestamps already carry, and the two can disagree — and that reading holds on a stream as it holds in the Store. The page subtracts inside one entry and renders `1m48s`; the wire carries the two instants it subtracted and never the subtraction, which is the same one-fact-two-notations rule that puts `repo_dirty` here and a `+` on the page.

**Its absence is what the page renders `reaped` for.** A reaped entry's only account is a `closed-by/` file, whose `ended_at` is the *closing* Run's instant on the closing Run's clock, so no duration derives (§7); the member is omitted and `closed_by` beside it says why. That is the ordinary absence rule carrying a fact rather than hiding one.

**`outcome` is the entry's own and is written always.** A window never names an open entry (StandingOf), so there is always one: the owner's where the entry holds an `outcome.json` — on a contested entry included — and `failed` where a closing write is its only account (§7).

**`closed_by` is every inference another Run drew**, and it stands beside the outcome rather than inside it. Putting a reaper's account in the outcome would be the surface deciding between two accounts of what the world did, which §7 is precise `hyper` does not do; leaving it off would be the tool holding a disagreement it never shows anyone. It is the same member `show` carries under the same name, and the page renders it as one stated line per file beneath the header.

**`repo_dirty` is written where the entry recorded it**, rather than the `+` suffix the page draws on the revision beside it: the bytes that Run read are nowhere in git, and this is the marker that stops a consumer resolving the revision and believing it read what ran (§7, §8).

**Nothing is abbreviated.** The Run id and the revision go out whole, as every id and every digest on this wire does (§8, ADR-0047).

type Standing

type Standing int

Standing is whether a Journal entry may be a side of a window, and where it may not, which of the two reasons it is.

The two are separate values because §8 is precise that they are separate facts. A rehearsal is **disqualified**: it performed the reads it reached and withheld the first effectful Step, and letting one be a baseline would retire the warning a real Run earned. An open entry is **not yet nameable**: it is not disqualified at all, it is an entry whose Run may be in flight or may be gone, and naming it would have the header render an outcome the entry does not have (§7, §8, ADR-0001).

A Probe is neither, because a Probe writes no Journal entry and can never reach this (ADR-0009).

const (
	// Nameable is an entry a window may name. **An outcome does not
	// disqualify one**: a refused Run's completed Steps reached the world
	// like any other's, so the triple is not consulted here at all.
	Nameable Standing = iota + 1
	// Rehearsal is an entry marked `dry_run`. It is disqualified as a
	// **baseline** under every form, and as the subject no rule may choose
	// — which is what Select and Preceding read this for. It is not
	// disqualified as a subject a caller **named**: `changes --subject`
	// reads past this value deliberately, asking what that Run read rather
	// than what the world became (§7, §8, ADR-0115).
	Rehearsal
	// Unclosed is an entry holding no account of how it ended: neither its
	// own Run's `outcome.json` nor another Run's closing write. It is
	// spelled for the state and not for the file, an entry closed by a
	// reaper being closed.
	Unclosed
)

func StandingOf

func StandingOf(entry store.Entry) Standing

StandingOf answers which of the three one entry is.

type Window

type Window struct {
	Procedure string
	Baseline  Side
	Subject   Side
}

Window is one Procedure's Comparison: the Procedure it is of, and the two ends it is between.

There is one per Procedure and never one across several. The baseline is the previous Run of the **same** Procedure, so a monitoring Run is never compared against a provisioning one — and that window is total rather than partial: every Run is a Run of a Procedure (ADR-0036), so no Run reaches the world outside some Procedure's Comparison.

func Select

func Select(entries []store.Entry, asked Selection) []Window

Select answers the windows a Comparison renders, one per Procedure, in Procedure-name code-point order.

entries is the Journal as the Store listed it and the order it is in does not matter: the two ends are chosen on `started_at`, which is the axis §9 orders Runs on and the only axis Runs have (ADR-0065).

**The subject is the newest nameable Run and the baseline is the one before it.** `--since` moves the baseline and never the subject: the baseline is the last nameable Run to have *started* before the instant, and everything after it folds into one rendering, so the subject stays the newest. Where no nameable Run started at or after the instant there is no window at all — nothing happened in the span the caller asked about, and a window whose two ends were one Run would render a Comparison of a Run against itself.

**A rehearsal and an open entry are passed over**, for the two different reasons StandingOf states, and a Procedure whose every entry is one of those names no window.

**Where no baseline exists the window carries none**, and the header states that as a named state — *no baseline — first Run of `<Procedure>`* — rather than rendering a line about a Run that is not there (§8).

type WindowRow

type WindowRow struct {
	Type      string   `json:"type"`
	Procedure string   `json:"procedure"`
	Baseline  *SideRow `json:"baseline,omitempty"`
	Subject   *SideRow `json:"subject"`
}

WindowRow is the Comparison's header: which two Runs are being compared, and everything the header says about each of them.

It is **one row and not one per line**, which is the shape §8 fixes and the precedent the review's own `artefact` row was written on: a header cites no line, so one row per rendered line would invent an anchor the surface does not have. It names its content — the window — rather than its position on a page, as every type in this stream does.

`baseline` is absent where there is none. A `window` row has exactly one way of having no baseline — the subject is the first Run of its Procedure — so the member's absence carries the whole of it, where the review's `artefact` row needs `baseline_absent` because four different absences reach that line (§8, §12).

func (WindowRow) Cells

func (r WindowRow) Cells() []string

Cells is empty: the header is a block of two labelled lines rather than a line in a table of like rows, and the page renders it as `changes` writes it (ADR-0026, and the review's `artefact` row one package over).

Jump to

Keyboard shortcuts

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