Skip to main content

Crate repon_core

Crate repon_core 

Source
Expand description

The rendering-agnostic core: it computes git state and knows nothing about terminals.

Its public surface is flat, re-exported from the crate root rather than through fanout, git, entity or snapshot, which stay private: a generic scatter primitive and a single branch read are not vocabulary a second consumer needs, and neither is which file happens to define EntityState or Snapshot. The one exception is liveness, a test-util-gated module of test waits that names nothing git-shaped and so belongs under a namespace rather than at the root. The entry points on Core itself (start, refresh, snapshot, try_settle, …) land in later work and get re-exported the same way once they exist.

§Reviewing an addition to this surface

Every addition to what this crate exports should hold each of these, from docs/spec/core-api.md’s ownership table and ADR 0015:

  • It does a git-shaped thing (discovery, a probe phase, the metadata poll, a Set boundary, an override, Generation supersession, the row fold, the display name, the default branch rung, the environment contract as data, Action fan-out), never a terminal-shaped one (rendering, the cursor, glyphs, theme, keybindings, the Launcher, config file discovery, $HOME, a user-specific environment variable).
  • An empty Selection carries no meaning here: this crate never defaults it to “the row under the cursor” or anything else that only makes sense on a screen.
  • refresh takes an already-ordered &[EntityKey]. This crate never computes or second-guesses that order; cursor-row-first is the consumer’s ordering to make, not this crate’s to infer.
  • A Filter is a pure predicate over these public types. Deciding when to apply one, if ever, stays with the consumer.
  • It has no notification channel, update stream or callback: a consumer reads a Snapshot when it decides to, it is never pushed one.

Four refusals hold for every type this crate ever makes public, reasoned in ADR 0015:

  • No #[non_exhaustive]: it forces a consumer to add a wildcard match arm even when every variant is already matched, reintroducing by attribute the default path ADR 0001 forbids. ADR 0015 argued this against an in-repo consumer; ADR 0021 observes that publishing widens the audience beyond that case, so the refusal stands but is not re-argued for the wider one.
  • No sealed trait: the enforcement ADR 0015 relies on is a real second consumer (repon sets), not defensive API ceremony guarding against a use case nothing presents.
  • No separate versioning scheme: repon-core is a path dependency with one in-workspace consumer, so a breaking change and its fix land in the same commit.
  • No git-backend trait abstraction: the crate’s existing test drives a real disposable repository rather than a mock, so a trait would buy testability already paid for.

Structs§

ActionReceipt
The most recent Action run against this Entity: a receipt of something Repon did, not a reading of the world, read by the row summary fold (docs/spec/actions.md, ADR 0018).
ActionSpec
One Action fan-out, crossing from the consumer as plain data: no TOML type, no confirm gate, no palette. label is what a receipt’s own label carries: the Action’s name, or the ad hoc command string typed into the palette. name is REPON_ACTION’s own value and is None for an ad hoc run, exactly as config.md’s environment contract already treats a Launcher’s absent REPON_ACTION. when is None for a built-in, a typed command or a configured entry that declares none, in which case every operable row runs; Some is what Core::run_action itself now decides the fan-out by, not only what a palette reports about it (docs/spec/actions.md’s “The Selection and the gate”).
AheadBehind
Commit counts ahead and behind an Entity’s branch’s upstream.
Applicability
How an Action’s when predicate divides the rows it would operate on, once a [[repo]] exclude = true has already been subtracted: three counts and no verdict.
AutoUpdateSpec
The fast-forward-only auto-update’s own crossing data (config.md’s [auto_update] table): whether it runs at all.
CaptureElision
What a step’s captured output lost to the head-plus-tail bound (docs/spec/actions.md’s “Capture”): two counts and no mark.
Cell
A displayed value together with its whole provenance.
Core
A running core: its own table, its own dedicated thread, and the rayon pool it shares with the rest of the process for probes.
CoreSpec
Everything Core::start needs, handed as plain data. The core reads no file, no path and no environment variable: this is the whole crossing, per docs/spec/core-api.md’s “What crosses from config”.
DefaultBranch
The default branch’s resolved name.
DeleteRisk
What accepting a delete confirm gate will destroy in one Repo or Worktree, per repo-management.md’s “The confirm gate”: the facts the gate names per row, read fresh at the moment the gate is built rather than folded out of Cells a Generation may have left stale. linked_worktrees is meaningful on a Repo row alone; a Worktree row’s own gate line never names it, since deleting one Worktree never touches its siblings.
Diagnostics
Per-Entity facts that are not Cells: which rung of the default branch resolution chain answered, whether rung 2 and rung 3 disagreed, why resolution stopped when it did not settle, and whether .gitmodules failed to read or parse.
DirtyCounts
The dirty cell’s settled value: phase C’s typed counts, per refresh.md’s “The phases”. A boolean is_dirty check was measured and rejected there: proving clean costs the same as counting, and a boolean cannot answer the untracked count at all, so this carries all three rather than folding them into one number at the probe.
Discovery
What one discovery walk found.
EntityKey
An Entity’s identity: a newtype over its own resolved absolute working directory.
EntityState
One Entity’s state: a struct of named Cells rather than a map, because the grid is not rectangular and each column carries its own payload type.
FetchFailures
The most recent periodic-fetch cycle’s own failures, read fresh through Core::fetch_failures the same way Core::discovery_warning and Core::vanished_count are: never latched, so a cycle where every fetch succeeds leaves this empty rather than carrying a stale failure forward from an earlier one. Per-repository independence is unaffected: one entry here is one repository run_fetch_cycle could not reach, never a reason another repository’s own fetch was skipped.
FetchSpec
The periodic fetch’s own crossing data (config.md’s [fetch] table): whether it runs at all, its cadence, and how many run at once. Plain bounding data, not the mutating mechanism itself: enabled: false here is what keeps the cycle from running, not the absence of any machinery to run it.
Filter
A Filter: a total, three-valued predicate parsed once from user text and matched against as many EntityState values as a consumer needs. See docs/spec/filter.md.
Generation
One refresh, so a newer one can be recognised over an older one still draining.
KeyVocabulary
One key’s own completion vocabulary, for the Filter line’s completion list (filter.md): its own text, and the fixed values docs/spec/filter.md’s vocabulary table gives it, empty for the three free-text keys.
ManagementHandle
The read-only, path-driven operations a management run’s per-row work needs (crates/repon/src/management.rs’s own run_one_record), cloned out of Core::management_handle rather than borrowed from a live Core: Send + 'static, so a caller can move it onto a background thread the way Core::run_action’s own fan-out thread already moves its Arc<RwLock<Table>> clone there. Grants none of Core’s other state (action_running, action_control, the clock thread): a management run is a distinct concern from the one fan-out those track, and this handle’s own methods touch only the table, exactly as Core::run_action_for_entity_blocking already does.
RecentCommit
One commit in an entity’s recent history: its seven-character abbreviated id and its message’s first line. Carries no provenance of its own, the same reasoning as InProgressOperation: it is read fresh alongside branch rather than tracked as a Cell.
RepoOverride
One Repo’s config-level override, crossing from the consumer as plain data: no TOML type, no ~ expansion left undone. config.md owns parsing it; the core only ever receives the result, and resolves path to the git common dir itself, since opening a repository is the core’s own work.
RunningStep
The step an ActionReceipt is executing right now, present only while its run has not yet finished (docs/spec/actions.md’s “The run on screen”: “a running step carries the spinner in the same position the step number’s outcome will occupy”).
SetSpec
A Set’s bounding specification, handed to the core as plain data: no TOML type, no file path, no ~ expansion left to do. config.md keeps the file format on the consumer’s side; this is what crosses the boundary.
Snapshot
The whole table, as a consumer reads it. Core::snapshot clones this every frame, so every field here, and everything reachable from it, is Clone, and every text-bearing value on an EntityState is an Arc<str> rather than a String precisely because of that per-frame clone.
Step
One command in an Action’s ordered list, crossing from the consumer as plain data: from_env already resolved, so this crate never learns what that means (config.md’s “Actions”, “Launchers” carries the same split for a Launcher’s own argv). shell crosses over unresolved, because resolving it is executor::run_step’s own job, the same convention the repon crate’s Launcher shell = true uses: with shell set, argv holds exactly one element, the whole command string, per config.md’s “Launchers”. env’s pairs are applied after environment::environment’s own set-or-unset pairs, so a step’s own env table overrides the guaranteed set exactly as config.md’s Launcher env field already does.
StepResult
One step’s own result within an ActionReceipt: its label, its outcome, its captured output and its elapsed time (docs/spec/actions.md’s “Where the result lives”).
Timestamp
A wall-clock moment, RFC 3339 on request via std::fmt::Display.

Enums§

AutoUpdateAttempt
One Core::attempt_auto_update result on a single Repo: the fast-forward-only auto-update’s own five eligibility rules (config.md’s eligibility rule), flattened into one return type so the built-in sync action (repo-management.md) can report every ineligible reason to the user rather than a bare “did nothing”.
DefaultBranchStopped
Why the default branch resolution chain reached rung 4, from facts the chain already has at no extra cost: which of gix’s own remote enumeration, or rung 3’s own name list, came up empty.
Head
HEAD’s three shapes, one to one with gix’s head::Kind.
InProgressOperation
One of the ten shapes an in-progress git operation can take, one to one with gix’s own state::InProgress (ADR 0019). Read from Repository::state(), which stats the per-worktree git dir’s own marker files rather than any Cell this crate probes, so it carries no provenance of its own: it is a fact of the moment it was read, not a value that can go stale or fail to resolve.
Kind
Which of the three domain objects an Entity is.
OwnWork
What a step Repon performed itself came to, in Repon’s own words (docs/spec/actions.md’s “Why the set grew from four to five”): three grades, matched exhaustively wherever a StepOutcome::OwnWork is opened.
Presence
Whether an Entity was found by the Refresh that just ran.
ProbeError
Error from a git read, cheap to clone because the whole state table is cloned every frame. A shared trait object was rejected: it gives no discriminant to branch on and nothing to serialise, and nothing in this crate reads a source chain.
RowSummary
The one state a row’s Cells fold into, for the gutter.
Settled
What a Cell has settled to. Never a bare Option<T>, so an absent value can never be read as a default.
Skip
Why a row’s Action receipt carries no steps and was never operated on, the three ways Core::run_action decides a row out before the fan-out starts (docs/spec/actions.md’s “The Selection and the gate”). Closed and matched exhaustively rather than three booleans, so a fourth reason to skip a row is a compile error here rather than a silent omission.
StepOutcome
One step’s own outcome: a closed set of exactly five (docs/spec/actions.md’s “Step outcomes”). No wildcard arm ever matches this: a sixth variant must be named at every match site or the crate fails to compile.
SyncState
The sync cell’s settled value: a live upstream’s ahead/behind counts, or one of two facts that preclude a count. NoRemote outranks NoUpstream, since a Repo with no remote at all makes every one of its rows, branch or not, unable to have an upstream in the first place (layout-and-provenance.md’s “Glyphs”).
Unknown
Why a Cell is Settled::Unknown. Closed at exactly these three: every other absence this design once modelled as Unknown turned out to be a settled value rendered elsewhere (a branch with no upstream renders -, a Repo with no remote renders ), so no NoUpstream or NoRemote reason exists here.
WorktreeState
The four mutually exclusive Worktree states, proven by ancestry or patch equivalence. Dirty is a separate, orthogonal cell, not a fifth arm here.

Functions§

count
Matches a Set against the boundary-stop walk with no probing and no provenance, for repon sets to print a count per declared Set. Infallible, like discover itself: an unreadable or missing root is silently zero entities rather than an error, so there is no DiscoveryError for this to return.
discover
Walks every root in spec, stopping at each Repo boundary, and returns the bounded list. Never descends into a boundary and never descends through a symlink, so a cycle cannot form by descent and needs no visited set or cycle detector to guard against; the set this module does keep exists only to give a symlink’s target the same identity as its real name, never to remember a path already walked. Never reads or writes a cache.
environment
entity’s environment contract: the set-or-unset pairs a Launcher or an Action step’s child receives. Some sets a variable, None unsets it, so an absent name can never be misread as one set to the empty string.
summary
Folds one Entity’s Cells into the state its row’s gutter shows.
vocabulary
The Filter’s whole vocabulary, one entry per key in docs/spec/filter.md’s own table order, read off the parser’s own closed key set so a key it accepts can never be missing from it and a key it does not accept can never be offered: this is the one place a consumer reaches that set from, rather than restating it.