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

domain

package
v0.0.0-...-f4d9c11 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package domain holds prv's pure types and derivations. It does no I/O.

Index

Constants

View Source
const (
	// MinServerWait and MaxServerWait bound every delay that came from the
	// server (Retry-After, reset time), so a hostile or broken value can
	// neither spin the loop nor park it for days.
	MinServerWait = time.Second
	MaxServerWait = 65 * time.Minute

	// MinFailureBase is the first retry delay after a failure when the
	// configured interval is shorter than this.
	MinFailureBase = 30 * time.Second
	// MaxFailureWait caps the exponential backoff (a longer configured
	// interval raises the cap to that interval).
	MaxFailureWait = 5 * time.Minute

	// RateLimitFloor is the least wait after a rate-limited failure that
	// carries no timing information.
	RateLimitFloor = time.Minute

	// ReservePerCost is how many refreshes' worth of budget prv keeps unused:
	// it pauses when Remaining <= ReservePerCost * max(Cost, 1).
	ReservePerCost = 10
)

Tunables of the refresh policy. They are judgement calls, not facts about the GitHub API: a wrong value costs latency, never correctness.

View Source
const (
	ReasonOtherHost = "only github.com PRs are supported"
	ReasonIssue     = "that is an issue, not a PR"
	ReasonNotRef    = "not a PR reference"
)

The reasons ParseRef gives (D-04).

View Source
const AllStates = StateSet(1<<StateOpen | 1<<StateMerged | 1<<StateClosed)

AllStates shows open, merged and closed PRs.

View Source
const DefaultClosedDays = 14

DefaultClosedDays is the default closed/merged window, in days.

View Source
const MaxPRNumber = 2147483647

MaxPRNumber is the largest GraphQL Int, the largest PR number prv accepts.

Variables

This section is empty.

Functions

func Bucket

func Bucket(pr PR) int

Bucket returns the attention bucket of a PR (D-01); a lower number needs attention sooner:

0 unavailable: a saved PR GitHub could not return (D-11)
1 open, failing CI
2 open, changes requested
3 open, approved and CI passing (ready to merge)
4 open, CI running
5 every other open PR (this includes approved with no checks or an
  unknown CI state: "no checks" never counts as passing)
6 open draft
7 merged
8 closed
9 unknown state

Lifecycle state is checked before the draft flag (D-04 as clarified on 2026-10-12): only an open draft sorts in bucket 6; a merged or closed draft sorts with merged or closed PRs.

func Clean

func Clean(s string) string

Clean makes a remote string safe to print in a terminal. It strips ANSI escape sequences, turns tabs into spaces, and drops C0/C1 control characters and the bidi override and isolate characters (U+202A..U+202E, U+2066..U+2069). Everything else, including ZWJ emoji sequences, passes through.

func InWindow

func InWindow(pr PR, now time.Time, closedDays int) bool

InWindow reports whether a PR belongs in the list. Open and unknown-state PRs are always kept, and so is every saved (Manual) PR whatever its state or close date: the window only decides which discovery PRs are kept (WATCH-07, D-13). A merged or closed PR that is not saved is kept only when it closed at or after now minus closedDays days (the boundary is inclusive); one with no close time cannot be placed in the window and is dropped.

func ServerWait

func ServerWait(now time.Time, retryAfter time.Duration, reset time.Time) time.Duration

ServerWait is the pause after a rate-limited response, built from that response's own timing. It uses the order and bounds of NextDelay: a positive retryAfter first, then a reset time after now plus one second, each kept within [MinServerWait, MaxServerWait]. Without usable timing it returns RateLimitFloor, so the result is always within [MinServerWait, MaxServerWait].

func Sort

func Sort(prs []PR)

Sort orders PRs in place for display: by attention bucket (see Bucket), then by creation time newest first, then repository ascending, then PR number ascending as an integer (D-02). The last-update time is deliberately not a key: comments and pushes would swap rows between refreshes. Equal keys keep their input order. Unavailable rows (bucket 0) have no creation time: they go by ref, the repository compared without letter case, then the number (D-11).

func SortChecks

func SortChecks(checks []Check)

SortChecks sorts checks in place into the detail view order (D-10): failing, cancelled, running, passing, skipped or neutral, unknown; within a class by lower-cased name, then workflow, then name. The same checks in any input order give the same output.

func ValidRef

func ValidRef(r Ref) bool

ValidRef reports whether r could name a pull request: OWNER/REPO with GitHub's character rules and a number from 1 to MaxPRNumber. It re-checks refs that come from a file a person may have edited.

Types

type CI

type CI int

CI is the check status shown for a pull request. The zero value is the non-favourable CINone ("no checks").

const (
	CINone CI = iota
	CIPassing
	CIFailing
	CIRunning
	CIUnknown
)

func ClassifyRollup

func ClassifyRollup(state *string) CI

ClassifyRollup maps statusCheckRollup.state to a CI value. nil (no checks) is CINone, never passing, and an unrecognised value is CIUnknown.

The rollup precedence is GitHub's own: a rollup with a CANCELLED run or a FAILURE beside running checks is FAILURE (01-SPIKE.md, extra finding 3). prv shows it without recomputing it (D-14). "Cancelled is never red" applies to per-check display in Phase 3, not to this list badge.

func (CI) Badge

func (c CI) Badge() string

Badge returns the CI badge: a glyph plus a word, readable without colour.

func (CI) Glyph

func (c CI) Glyph() string

Glyph returns the one-cell glyph at the start of the CI badge.

type Check

type Check struct {
	Name     string // check-run name, or the status context name
	Workflow string // workflow name, else the check suite's app name, else ""
	Source   string // detail view only: the workflow file's base name, else the app name; "" for a status
	Context  bool   // true for a commit StatusContext
	State    CheckState
	Word     string // lowercase display word, e.g. "failure", "timed out"

	SuiteCreatedAt time.Time // when the check suite was created (orders re-runs)
	ID             int64     // check-run databaseId; zero for a status context
}

Check is one check run or commit status of a pull request, already mapped and sanitised.

func LatestChecks

func LatestChecks(checks []Check) []Check

LatestChecks keeps one check per (Context, Workflow, Name): the one from the newest check suite (SuiteCreatedAt, then the larger ID). A job that failed and then passed in a newer run is therefore not failing (D-03). On a full tie the earlier check in the input stays. The output keeps the input order of each key's first appearance; the input is not modified.

type CheckCounts

type CheckCounts struct {
	Failing, Cancelled, Running, Passing, Neutral, Other int
}

CheckCounts is how many checks are in each display class. Neutral holds skipped, neutral and stale checks. Other holds the checks of class CheckOther (unknown), which are never counted as skipped.

func CountChecks

func CountChecks(checks []Check) CheckCounts

CountChecks counts checks by class. Callers pass the de-duplicated checks (LatestChecks), so a superseded re-run is never counted (D-03).

type CheckState

type CheckState int

CheckState is the display class of one check (a check run or a commit status). The zero value is CheckOther: an unrecognised value is never failing and never passing.

const (
	CheckOther CheckState = iota
	CheckFailing
	CheckCancelled
	CheckRunning
	CheckPassing
	CheckNeutral
)

func ClassifyCheck

func ClassifyCheck(typename, status, conclusion, state string) (CheckState, string)

ClassifyCheck maps GitHub's check values to a CheckState and a display word. typename is "StatusContext" for a commit status (which reads state); any other typename is a check run (which reads status, and conclusion once it is COMPLETED). The mapping is total: any value it does not know is (CheckOther, "unknown"), never failing and never passing.

func (CheckState) Glyph

func (s CheckState) Glyph() string

Glyph returns the one-cell glyph of the state.

type Delay

type Delay struct {
	Wait        time.Duration // time until the next refresh
	PausedUntil time.Time     // when set, no request may go out before this; zero means no pause
}

Delay is the decision of NextDelay.

func NextDelay

func NextDelay(now time.Time, interval time.Duration, failures int, rl RateLimit, last Outcome) Delay

NextDelay decides how long to wait before the next refresh. It is pure: the current time is the now parameter. failures counts consecutive failures, including the one in last. rl is the last known rate limit.

Order: a Retry-After wins; then a known reset time; then a bounded exponential. On success the reserve pause and pacing apply.

type Detail

type Detail struct {
	Checks    []Check
	Reviewers []Reviewer
	Counts    CheckCounts
	Truncated bool
	Partial   bool
	Read      int
	Total     int
}

Detail is the full detail of one pull request.

Checks are de-duplicated (LatestChecks) and sorted (SortChecks). Counts are always CountChecks(Checks), the de-duplicated checks, never GitHub's own per-state counts, which include superseded runs (D-03).

Truncated means that not every check run was read: paging stopped at the page cap or at an empty cursor, a later page failed, or fewer runs were read than GitHub reports. Read is the number of check runs read and Total the number GitHub reports, so the view can say "read N of M" (D-03, D-08).

Partial means that GitHub returned a GraphQL error next to the data of some page, so a check, a reviewer or a review request may be missing. Partial says nothing about the run count (G-03-6, WR-05).

type FailingCheck

type FailingCheck struct {
	Known     bool
	Name      string
	Cancelled bool // the named check is cancelled, and nothing else fails
	More      int  // how many other checks fail
}

FailingCheck is the failing check named in a PR's CI cell (LIST-07). The zero value means "no name: show the plain badge".

func SummarizeFailing

func SummarizeFailing(latest []Check, complete bool) FailingCheck

SummarizeFailing picks the check a failing row names from the de-duplicated checks (LatestChecks). complete is false when the contexts page was truncated: then no name is known, because a re-run on the missing page may have fixed it.

A failing check is named before any cancelled one (D-04): the name is the failing check that sorts first by lower-cased name, and More counts the other failing checks. Cancelled checks are never added to that count, so the row agrees with a detail summary that shows failing and cancelled separately. When no check fails, the first cancelled check is named with Cancelled set, and More counts the other cancelled checks. Counts come from the de-duplicated checks, never from GitHub's per-state counts (D-02). No failing or cancelled check gives the zero value.

type Merge

type Merge int

Merge is the merge-readiness shown for an open pull request. The zero value is MergeNone, a blank cell: merged, closed and unknown-state PRs.

const (
	MergeNone Merge = iota
	MergeUnknown
	MergeMergeable
	MergeBlocked
	MergeBehind
	MergeConflicts
)

func DeriveMerge

func DeriveMerge(st State, draft bool, status, mergeable string) Merge

DeriveMerge returns the merge-readiness of a pull request from its state, draft flag and GitHub's mergeStateStatus and mergeable values.

State and draft are decided first (RESEARCH F4): a merged PR reports UNKNOWN, a closed PR keeps a stale BLOCKED or DIRTY, and a draft reports BLOCKED or DIRTY because the enum has no DRAFT value. Only then is the status read. CLEAN, HAS_HOOKS and UNSTABLE are mergeable (D-07: the CI column already shows a failing check). UNKNOWN is common and transient (F5); it is a dash, except that mergeable CONFLICTING means conflicts. An empty or unrecognised status is a dash, never mergeable, so a PR cannot look more ready than GitHub's data supports.

func (Merge) Badge

func (m Merge) Badge() string

Badge returns the merge badge: a glyph plus a word, readable without colour. MergeUnknown is a bare dash and MergeNone is blank.

func (Merge) Glyph

func (m Merge) Glyph() string

Glyph returns the one-cell glyph at the start of the badge. Compact rows show it alone; TestMergeGlyphPrefix keeps it equal to the badge's first glyph. MergeNone is a space so a compact row keeps its columns.

type Outcome

type Outcome struct {
	Failed      bool
	RateLimited bool          // the failure was a rate limit
	RetryAfter  time.Duration // from a Retry-After header; zero when absent
	ResetAt     time.Time     // reset time from the error; zero when absent
}

Outcome describes the last fetch to NextDelay.

type PR

type PR struct {
	ID      string
	Ref     Ref
	Title   string
	URL     string
	Author  string
	IsDraft bool
	State   State
	Review  Review
	CI      CI
	// Failing is the failing check named in the CI cell (LIST-07); the zero
	// value shows the plain badge.
	Failing FailingCheck
	// Merge is the merge-readiness (LIST-06); the zero value is a blank cell.
	Merge Merge
	// Reviewers are the people and teams on the PR in D-06 order (LIST-04).
	Reviewers []Reviewer

	// Auto is true when discovery found the PR (the viewer authored it or is
	// assigned to it). Manual is true when watchlist.json holds it. A PR can be
	// both (WATCH-03).
	Auto, Manual bool
	// Watched is the saved entry's ref, set when Manual. It can differ from Ref
	// after a repository rename; removal goes by it.
	Watched Ref
	// Unavail is set when a saved PR could not be loaded; the zero value means
	// the PR loaded.
	Unavail Unavail

	CreatedAt time.Time
	UpdatedAt time.Time
	ClosedAt  time.Time
}

PR is one watched pull request, already mapped and sanitised.

func Combine

func Combine(auto, looked []PR) []PR

Combine merges the discovery results with the looked-up saved PRs into a new slice: auto in order, then each looked PR in order. A looked PR whose non-empty ID equals the ID of a PR already kept is folded into that PR, which becomes Manual with the looked PR's Watched ref, and is not appended: a renamed repository is one row (WATCH-03). A looked PR with an empty ID (an unavailable one) is always appended. Neither input is changed.

func Reconcile

func Reconcile(prev, next []PR, strikes map[Ref]int) ([]PR, map[Ref]int)

Reconcile applies the D-12 rule to the rows of a successful refresh and returns the rows to show and the new strike counts. It is pure: it returns a new slice and a new map and changes neither input.

A row of next that is unavailable with an "other" error (not NOT_FOUND, not SSO) is a strike for its saved ref (Watched.Key()). On the first strike a PR that prev shows as loaded keeps its last data, so one bad answer does not flicker the row; on the second strike in a row, or when there is no last data (the first launch), it stays unavailable. NOT_FOUND and SSO are real answers and show at once. A row that loads adds no key, so its count starts over, and a key whose row is gone is dropped. The rows are sorted again when a row was replaced by its last data, because that row's bucket changed; otherwise the order of next is kept as it is.

A refresh that fails as a whole (network, 5xx, 401, rate limit) never reaches Reconcile: the caller keeps the rows and the counts as they are. Reconcile never touches the saved list: a PR that stays unavailable stays saved.

func Visible

func Visible(prs []PR, show StateSet, number string) []PR

Visible returns the PRs the filters let through, in input order, as a new slice; prs is not changed. A PR passes when its state is in show and, if number is not empty, its PR number starts with the digits in number (so "12" keeps #12, #120 and #1234, never #312). A PR in an unknown state is never hidden by show (same rule as InWindow): it must not vanish silently. The same goes for an unavailable row, whose state is not known (D-13). The number filter still applies to both.

type RateLimit

type RateLimit struct {
	Limit     int
	Cost      int
	Remaining int
	ResetAt   time.Time
}

RateLimit is the GraphQL rate limit data returned with a query.

type Ref

type Ref struct {
	Repo   string // owner/repo
	Number int
}

Ref identifies a pull request by repository and number.

func MarkWatched

func MarkWatched(prs []PR, saved []Ref) []Ref

MarkWatched sets Manual and Watched on every PR in prs whose Ref has the same Key as a saved ref, and returns the saved refs that no PR matched, in input order and without repeats (compared by Key). prs is changed in place, as Sort does. A saved PR that discovery already returned costs no Lookup.

func ParseRef

func ParseRef(s string) (Ref, error)

ParseRef reads one pasted PR reference: a github.com PR URL (scheme and www. optional; /files, /checks, /commits or /commits/SHA, a query or a fragment may follow the number) or OWNER/REPO#N. Only the owner, repository and number are kept; the owner and repository keep the letter case that was typed. Every failure is a *RefError whose Reason says why (D-04).

func (Ref) Key

func (r Ref) Key() Ref

Key returns the ref in the form used to compare refs: owner and repository in lower case (GitHub names are case-insensitive), number unchanged. It is the key for duplicates, saved entries and removal (D-03).

func (Ref) String

func (r Ref) String() string

String returns "owner/repo#123".

func (Ref) URL

func (r Ref) URL() string

URL returns the browser URL of the pull request.

type RefError

type RefError struct{ Reason string }

RefError is a pasted PR reference that ParseRef refused. Reason is the text shown in the add prompt.

func (*RefError) Error

func (e *RefError) Error() string

type RequestInput

type RequestInput struct{ Typename, Name string }

RequestInput is one review request. Typename is "" when GitHub returned the requested reviewer as null.

type Review

type Review int

Review is the review status shown for a pull request. The zero value is the non-favourable ReviewNone.

const (
	ReviewNone Review = iota
	ReviewApproved
	ReviewChangesRequested
	ReviewRequired
)

func DeriveReview

func DeriveReview(decision string, opinionated []string) Review

DeriveReview returns the review status shown for a pull request.

GitHub's reviewDecision wins when it is set. It is null on repositories without a required-review rule even when approvals exist, so a null (or unknown) decision falls through to the opinionated review states: any CHANGES_REQUESTED wins over approvals whatever the order, else any APPROVED gives approved, else none. Derivation never yields ReviewRequired, so a null decision is never shown as "review required".

func (Review) Badge

func (r Review) Badge() string

Badge returns the review badge: a glyph plus a word, readable without colour. "– none" is the badge for "no review status".

func (Review) Glyph

func (r Review) Glyph() string

Glyph returns the one-cell glyph at the start of the review badge. Compact rows show it alone; TestGlyphPrefix keeps it equal to the badge's first glyph.

type ReviewInput

type ReviewInput struct{ Login, State string }

ReviewInput is one review. Strings are already cleaned by the github seam. Login is "" when the review's author is null.

type Reviewer

type Reviewer struct {
	Name  string
	Kind  ReviewerKind
	State ReviewerState
}

Reviewer is one reviewer of a pull request.

func MergeReviewers

func MergeReviewers(opinionated []ReviewInput, requested []RequestInput, latest []ReviewInput) []Reviewer

MergeReviewers merges review requests and reviews into the reviewers of one pull request, in display order.

Every request is a pending reviewer. A reviewer who reviewed and was then re-requested stays in opinionated, so pending wins (RESEARCH F7); logins are compared case-insensitively and the requested spelling is kept. A request whose reviewer is null is a hidden pending reviewer and is never dropped. A review whose author is null is skipped. opinionated holds only approvals and change requests (list rows); latest adds commented and dismissed reviewers for the detail view and never overrides a reviewer already present.

type ReviewerKind

type ReviewerKind int

ReviewerKind says what a Reviewer names.

const (
	ReviewerPerson ReviewerKind = iota // a user, bot or mannequin login
	ReviewerTeam                       // a team request with a visible slug
	// ReviewerHidden is a request whose reviewer GitHub returned as null,
	// normally a team the viewer cannot see (RESEARCH F6).
	ReviewerHidden
)

type ReviewerState

type ReviewerState int

ReviewerState is one reviewer's state on a pull request. The zero value is ReviewerPending, the least favourable known state.

const (
	ReviewerPending ReviewerState = iota
	ReviewerChangesRequested
	ReviewerApproved
	ReviewerCommented // detail view only
	ReviewerDismissed // detail view only
)

func (ReviewerState) Glyph

func (s ReviewerState) Glyph() string

Glyph returns the one-cell glyph of the state.

func (ReviewerState) Word

func (s ReviewerState) Word() string

Word returns the display word of the state.

type Snapshot

type Snapshot struct {
	Viewer    string
	PRs       []PR
	Truncated bool
	Warnings  []string
	RateLimit RateLimit
}

Snapshot is the result of one fetch.

type State

type State int

State is the lifecycle state of a pull request.

const (
	StateOpen State = iota
	StateMerged
	StateClosed
	StateUnknown
)

The zero value is StateOpen. An unrecognised remote value is StateUnknown.

func ParseState

func ParseState(s string) State

ParseState maps GitHub's PullRequestState enum to a State. The mapping is total: any other value is StateUnknown, never StateOpen.

func (State) String

func (s State) String() string

String returns the lowercase display name of the state.

type StateSet

type StateSet uint8

StateSet is a set of the lifecycle states the user wants to see (D-10). The unknown state has no bit: a PR in an unknown state is never filtered out.

func (StateSet) Has

func (s StateSet) Has(st State) bool

Has reports whether st is in the set.

func (StateSet) Toggle

func (s StateSet) Toggle(st State) StateSet

Toggle returns the set with st added when it was absent and removed when it was present.

type Unavail

type Unavail struct {
	Kind   UnavailKind
	Reason string
	Help   string
}

Unavail describes a saved PR that could not be loaded. Reason is the short text for rows and the add prompt (D-10); Help is the longer text for the detail view.

func LoadFailed

func LoadFailed(short, help string) Unavail

LoadFailed is the Unavail of a PR that failed to load for another reason.

func NotFound

func NotFound() Unavail

NotFound is the Unavail of a PR that GitHub reports as not found.

func SSORequired

func SSORequired(org string) Unavail

SSORequired is the Unavail of a PR in an organization that requires SSO authorization for the token (D-10). org must be the owner of a ref that passed ValidRef (letters, digits and hyphens), never text taken from a message.

type UnavailKind

type UnavailKind int

UnavailKind says why a saved PR could not be loaded.

const (
	UnavailNone UnavailKind = iota
	UnavailNotFound
	UnavailSSO
	UnavailOther
)

UnavailNone is the zero value: the PR loaded.

Jump to

Keyboard shortcuts

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