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

board

package
v0.145.2 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package board picks the next item of work from a GitHub project board: it reads the board's open items once, ranks them by the configured order and says for every item above the pick why it was skipped. Which items a session already serves is the caller's to decide; the rank is pure.

Index

Constants

View Source
const (
	StatusField = "Status"
	KindField   = "Kind"
	TeamField   = "Team"
)

The board's fields the order and the moves read.

View Source
const LeaseLabel = "lease/"

LeaseLabel is the prefix of an issue label naming a lease the work needs: lease/agentlab-1.

View Source
const PriorityField = "Priority"

PriorityField is the board's number field that holds the priority list: an epic's rank, 1 the top. Only the person sets it (beekeeper priority set --as <person>); board next applies it.

Variables

This section is empty.

Functions

func ByPriority added in v0.131.0

func ByPriority(cands []Candidate, r Ranks, parent func(string) string)

ByPriority orders cands by the rank each has (Ranks.Of, from its own parent and upward through parent) first, unranked after ranked, and keeps their order within a rank: the priority list over the configured order. It records the rank applied on each candidate.

func Matches

func Matches(st config.BoardStep, it Item, now time.Time) bool

Matches reports whether the step st offers the board item it.

func Ref

func Ref(s string) (owner, repo string, n int, err error)

Ref parses owner/repo#n or an issue's GitHub URL.

func Resolve

func Resolve(field string, values []string, in string) (string, error)

Resolve returns the one value of values that in stands for: the value itself, or the only one whose words (case, emoji and punctuation ignored) begin with or contain in's. Anything else is an error that lists the values.

func ResolveOrder

func ResolveOrder(order []config.BoardStep, statuses, kinds []string) ([]config.BoardStep, error)

ResolveOrder returns order with its Status and Kind values replaced by the board's canonical ones.

func Unoffered added in v0.60.3

func Unoffered(order []config.BoardStep, it Item, now time.Time) string

Unoffered says why no step of order offers the board item it on its own, the first refusal of a step that selects it; empty when a step offers it.

Types

type Candidate

type Candidate struct {
	Item
	// Step is the name of the step that offers it; Epic the epic whose
	// sub-issue it is.
	Step string `json:"step"`
	Epic string `json:"epic,omitempty"`
	// Skip says why it is not free, empty when it is.
	Skip string `json:"skip,omitempty"`
	// Priority is the rank ByPriority applied, 0 for none; PriorityOf the
	// epic it is the rank of, the candidate itself or an ancestor.
	Priority   int    `json:"priority,omitempty"`
	PriorityOf string `json:"priorityOf,omitempty"`
}

Candidate is an item the order offers, in its place.

func Rank

func Rank(snap *Snapshot, b config.Board, now time.Time) []Candidate

Rank returns the candidates of every step in order, each item once, in the first step that matches it. A SubIssues step offers an item with open sub-issues through them (its remainder) and is the item's only step; an item without is offered itself. A sub-issue that is a board item, of any Team or Status, is held to the order like any other: one no step offers on its own (an old Backlog item, a blocked one, one in Inbox) is marked skipped with the reason. Items with an open recorded blocker, without activity for b.StaleAfter or assigned to anybody outside b.People are marked skipped too. The epic gate (snap.Gate) holds an epic in planning and every issue under it whatever the step: the epic is marked skipped with its plan, never offered through its sub-issues, and an issue whose nearest epic among its parents on the board is in planning is marked skipped naming it. What GitHub answered with an error is never read as absent: a sub-issue whose board fields are unknown takes them from its item among snap.Items, else is marked skipped naming them, and an item whose sub-issues are unknown is offered skipped itself.

func (Candidate) PriorityText added in v0.131.0

func (c Candidate) PriorityText() string

PriorityText says the rank applied: "priority 2", "priority 2 of epic owner/repo#n" when inherited; "" for none.

func (Candidate) Why

func (c Candidate) Why() string

Why says why a free candidate is picked.

type Client

type Client struct {
	GH    github.GH
	Board config.Board
	Plans []string
}

Client reads and moves the items of one board through the GitHub GraphQL API; Plans are the plans repositories (owner/repo) an epic's plan is found in.

func (*Client) AnswerThread added in v0.142.0

func (c *Client) AnswerThread(ctx context.Context, ref string) (*Thread, error)

AnswerThread is the thread an answer about ref is recorded on: one read of ref and its parents acting on ref's repository, and one search for the plans naming it or its parents when its repository is public. A ref that does not exist, or a public one with no private thread, is refused.

func (*Client) Block added in v0.128.0

func (c *Client) Block(ctx context.Context, ref, by string) (*Held, error)

Block records the issue by as a blocker of the issue ref, a GitHub blocked-by dependency (addBlockedBy): board next skips ref while one of its blockers is open and offers it again once they closed, with no session acting. A dependency recorded already is left as it is.

func (*Client) Closes added in v0.102.0

func (c *Client) Closes(ctx context.Context, refs []string) (map[string][]string, error)

Closes reads the issues each pull request among refs closes (its closing references, the Closes and Fixes keywords resolved across repositories), keyed by the lower-cased ref as given, closesBatch refs of one owner per request, acting on their repository (github.ActingOn). An issue among refs, or a ref that is no issue, closes nothing; nil when nothing was asked.

func (*Client) Comment added in v0.142.0

func (c *Client) Comment(ctx context.Context, t *Thread, body string) (string, error)

Comment posts body on the thread, acting on its repository, and returns the comment's URL.

func (*Client) Epics added in v0.127.0

func (c *Client) Epics(ctx context.Context) ([]Epic, error)

Epics reads the board's epics in planning or past their go, the team's, with their plans: one request for the fields, one per 100 items, one per plansBatch epics. Nil when the gate is off.

func (*Client) FindPlans added in v0.127.0

func (c *Client) FindPlans(ctx context.Context, refs []string) (map[string]*Plan, error)

FindPlans finds the plan of each epic of refs, keyed by the lower-cased ref: one search of the plans repositories (Client.Plans) per epic for pull requests naming it, plansBatch searches per request, acting on the first plans repository. Of those whose description's Epic line names the epic (its URL or owner/repo#n), the most recently updated open one is the plan, else the most recently updated merged one; a closed one never is. Nil when no plans repository is configured.

func (*Client) Move

func (c *Client) Move(ctx context.Context, ref, in string) (*Moved, error)

Move sets the Status of the board item of the issue ref to the status in stands for (see Resolve): the item the issue's projectItems name, else the one addProjectV2ItemById answers, which is the issue's existing item or a new one, with board.team as its Team when it has none; no read of the board's items. An issue of a repository outside the board's owner whose projectItems name no item of the board is never added to it: its item on a project of its repository's own owner (a personal account's user project) moves there by that project's Status values, Moved.Board naming it, without a Team or the epic gate; with no such item, or more than one, it is refused, naming the recipe. Every call acts on the issue's repository (github.ActingOn), so a gh link choosing its login by the owner reaches the issue. An unknown or ambiguous status, and a board.team that is none of the Team field's values, are refused with the board's values before anything is written; an issue GitHub does not resolve is refused as no issue, with GitHub's reason. A move of an epic to the epic gate's go status is the go: Moved.Go names its plan and the private thread to record it on (RecordGo), and an epic with a plan but no private thread is refused before anything is written.

func (*Client) Open added in v0.73.2

func (c *Client) Open(ctx context.Context, ref string) (bool, error)

Open reports whether the issue or pull request ref is open, acting on its repository (github.ActingOn).

func (*Client) Priorities added in v0.131.0

func (c *Client) Priorities(ctx context.Context) (Priorities, error)

Priorities reads the priority list: the open epics of board.team (the epic gate's kinds) whose Priority field holds a rank, by rank and, within one, in the board's order. A board without a Priority field has an empty list. It is the one read every consumer of the priorities calls (board next; lease, lanes and capacity next), with Ranks and Ranks.Of on top.

func (*Client) Rank added in v0.126.0

func (c *Client) Rank(ctx context.Context, ref string, at Place) (*Ranked, error)

Rank moves the board item of the issue ref to the place at with updateProjectV2ItemPosition, the board's position being the order board next reads its items in. An issue without an item is added first, as Move adds it, with board.team as its Team. The item of a Before or After issue is found by that issue's projectItems and must be on the board already; Before is two moves, the item after the other one and the other one after the item, so nothing reads the board's items to find the other one's predecessor. Each call acts on the repository of the issue it asks about. No issue, a reference without an item on the board and an item placed against itself are refused before anything is written.

func (*Client) Read

func (c *Client) Read(ctx context.Context, now time.Time) (*Snapshot, error)

Read reads the board items the order can offer and the epic gate holds, the open sub-issues of the items a SubIssues step matches, the search steps' issues and the plans of the epics the gate watches: one request for the fields, one per 100 items, one per extrasBudget sub-issues for the sub-issues and searches, one per plansBatch epics.

func (*Client) RecordGo added in v0.127.0

func (c *Client) RecordGo(ctx context.Context, g *Go, body string) (string, error)

RecordGo posts the go's comment on its private thread, acting on the thread's repository, and returns the comment's URL.

func (*Client) SetPriority added in v0.131.0

func (c *Client) SetPriority(ctx context.Context, ref string, rank int) (*Prioritized, error)

SetPriority puts the epic ref at rank in the priority list, 1 the top: the epics at and below the rank move down one, and the list stays ranked 1..n (a rank past its end is its end). rank 0 takes the epic off the list, the epics below it moving up one. Only the items whose rank changes are written, each by its id; the list is read with Priorities, the epic's item from its projectItems (added to the board as Move adds one). The caller is the one to check that the person asked for it. Refused before anything is written: a board without a number field Priority, an issue that does not exist, is closed or is no epic of the epic gate's kinds.

func (*Client) Unblock added in v0.128.0

func (c *Client) Unblock(ctx context.Context, ref, by string) (*Held, error)

Unblock removes the issue by from the blockers of the issue ref (removeBlockedBy). A dependency not recorded is left as it is.

type Epic added in v0.127.0

type Epic struct {
	Ref    string `json:"ref"`
	URL    string `json:"url"`
	Title  string `json:"title"`
	Status string `json:"status"`
	Plan   *Plan  `json:"plan"`
}

Epic is an epic the gate watches, in planning or past its go, with its plan, nil when no plan pull request names it.

func (Epic) PlanText added in v0.127.0

func (e Epic) PlanText() string

PlanText is the plan in a few words: its URL, its state and its head.

type Gate added in v0.127.0

type Gate struct {
	Kind     []string
	Planning []string
	Go       string
}

Gate is the epic gate (config.EpicGate) in the board's canonical values; the zero Gate is off.

func ResolveGate added in v0.127.0

func ResolveGate(e config.EpicGate, statuses, kinds []string) (Gate, error)

ResolveGate resolves e against the board's statuses and kinds. A value board.epics names must be the board's; a default the board has not turns the gate off (no epic kind, no planning status) or the go unrecorded.

func (Gate) Epic added in v0.127.0

func (g Gate) Epic(it Item) bool

Epic reports whether the board item it is an epic.

func (Gate) InPlanning added in v0.127.0

func (g Gate) InPlanning(it Item) bool

InPlanning reports whether it is an epic in planning.

type Go added in v0.127.0

type Go struct {
	Epic   string `json:"epic"`
	Plan   *Plan  `json:"plan,omitempty"`
	Thread string `json:"thread,omitempty"`
	// Comment is the URL of the comment that records the go.
	Comment string `json:"comment,omitempty"`
	// contains filtered or unexported fields
}

Go is the go a move is: an epic moved to the gate's go status. Plan is its plan, nil for none, and Thread the private thread the go is recorded on (owner/repo#n): the epic itself in a private repository, else its plan pull request in a private one.

func (*Go) Text added in v0.127.0

func (g *Go) Text(by, status string) string

Text is the comment that records the go, by whom it was moved.

type GraphQLError added in v0.129.0

type GraphQLError struct {
	Path    []string
	Type    string
	Message string
}

GraphQLError is one error GitHub answered beside the data: Path is the field it could not resolve (aliases, field names and list indexes), Type GitHub's kind of error (NOT_FOUND, RESOURCE_LIMITS_EXCEEDED, …).

type Held added in v0.128.0

type Held struct {
	Ref string `json:"ref"`
	By  string `json:"by"`
	// ByOpen says the blocker is open: the hold keeps the item from board
	// next until it closes.
	ByOpen bool `json:"byOpen"`
	// Changed says the dependency was added or removed; false when it was
	// recorded already (block) or not recorded (unblock).
	Changed bool `json:"changed"`
	// Blockers and OpenBlockers are the issue's recorded blockers after the
	// change, all and the open ones.
	Blockers     int `json:"blockers"`
	OpenBlockers int `json:"openBlockers"`
}

Held is a hold recorded on an issue, or removed from it: the issue's GitHub blocked-by dependency on another issue, which board next reads.

type Item

type Item struct {
	// Ref is owner/repo#n.
	Ref   string `json:"ref"`
	URL   string `json:"url"`
	Title string `json:"title"`
	// OnBoard says it is an item of the board project, Status and Kind
	// its board fields.
	OnBoard   bool      `json:"onBoard,omitempty"`
	Status    string    `json:"status,omitempty"`
	Kind      string    `json:"kind,omitempty"`
	Labels    []string  `json:"labels,omitempty"`
	Assignees []string  `json:"assignees,omitempty"`
	Created   time.Time `json:"created"`
	Updated   time.Time `json:"updated"`
	// Blockers counts the recorded blockers, OpenBlockers the open ones.
	Blockers     int `json:"blockers,omitempty"`
	OpenBlockers int `json:"openBlockers,omitempty"`
	// OpenSubIssues counts the open sub-issues; SubIssues are they, read
	// for a SubIssues step only.
	OpenSubIssues int    `json:"openSubIssues,omitempty"`
	SubIssues     []Item `json:"subIssues,omitempty"`
	// Parent is the issue it is a sub-issue of, owner/repo#n.
	Parent string `json:"parent,omitempty"`
	// Missing names the fields of it GitHub answered with an error instead
	// of their value ("<field>: <message>"): they are unknown, not absent.
	Missing []string `json:"missing,omitempty"`
	// contains filtered or unexported fields
}

Item is an open issue: a board item or a sub-issue or search result.

func (Item) Leases added in v0.82.0

func (it Item) Leases() []string

Leases are the leases the item's labels say its work needs.

type Moved

type Moved struct {
	Ref  string `json:"ref"`
	From string `json:"from,omitempty"`
	To   string `json:"to"`
	// Added says the issue had no item on the board: one was added before
	// the move. Team is the Team the item was given, board.team, "" when
	// it had one or the board names none.
	Added bool   `json:"added,omitempty"`
	Team  string `json:"team,omitempty"`
	// Go is the go the move is, an epic moved to the epic gate's go
	// status; nil for any other move.
	Go *Go `json:"go,omitempty"`
	// Board is the project the item moved on when it is not the configured
	// board: the project of the issue's repository's own owner
	// (owner/number); "" for the configured board.
	Board string `json:"board,omitempty"`
}

Moved is a move of an item to a status.

type Partial added in v0.129.0

type Partial struct{ Errors []GraphQLError }

Partial is an answer with errors beside its data: the data is decoded, and every field an error's path names is missing from it, never absent.

func (*Partial) Error added in v0.129.0

func (p *Partial) Error() string

type Place added in v0.126.0

type Place struct {
	Top           bool
	Before, After string
}

Place is where Rank puts an item: at the top of the board, or right before or right after the item of another issue, one of the three.

type Plan added in v0.127.0

type Plan struct {
	Ref   string `json:"ref"`
	URL   string `json:"url"`
	Title string `json:"title"`
	// State is draft, open or merged; Head the head commit.
	State   string `json:"state"`
	Head    string `json:"head"`
	Private bool   `json:"private"`
	// contains filtered or unexported fields
}

Plan is an epic's plan: the open or merged pull request of a plans repository whose description names the epic.

type Priorities added in v0.131.0

type Priorities []RankedEpic

Priorities is the priority list, by rank.

func (Priorities) Ranks added in v0.131.0

func (p Priorities) Ranks() Ranks

Ranks is the priority list as each epic's rank by its lower-cased ref.

type Prioritized added in v0.131.0

type Prioritized struct {
	Ref  string     `json:"ref"`
	From int        `json:"from,omitempty"`
	To   int        `json:"to,omitempty"`
	List Priorities `json:"list"`
	// Added says the epic had no item on the board, Team the Team it was
	// given; as in Moved.
	Added bool   `json:"added,omitempty"`
	Team  string `json:"team,omitempty"`
}

Prioritized is a change of the priority list: the epic, its rank before and after (0 for none), and the list as it is now.

type Ranked added in v0.126.0

type Ranked struct {
	Ref string `json:"ref"`
	// Above and Below are the item's new neighbours that the move itself
	// names: the item it went after or before, and for the top the item
	// now below it; "" for one no request read. A neighbour that is no
	// issue is "a draft".
	Above string `json:"above,omitempty"`
	Below string `json:"below,omitempty"`
	Top   bool   `json:"top,omitempty"`
	// Added and Team are as in Moved: the issue had no item on the board.
	Added bool   `json:"added,omitempty"`
	Team  string `json:"team,omitempty"`
}

Ranked is a move of an item to a place on the board.

type RankedEpic added in v0.131.0

type RankedEpic struct {
	Rank   int    `json:"rank"`
	Ref    string `json:"ref"`
	URL    string `json:"url"`
	Title  string `json:"title"`
	Status string `json:"status,omitempty"`
	// contains filtered or unexported fields
}

RankedEpic is an epic of the priority list.

type Ranks added in v0.131.0

type Ranks map[string]int

Ranks are the epics' ranks by their lower-cased ref.

func (Ranks) Of added in v0.131.0

func (r Ranks) Of(ref string, parent func(string) string) (int, string)

Of is the rank ref has, its own or inherited from its nearest ranked ancestor through the native sub-issue relation (parent says an issue's parent, "" when unknown), and the epic it is the rank of; 0 and "" for an issue without a ranked ancestor.

type Refusal

type Refusal struct{ Reason string }

Refusal is a move the board's values or items do not allow.

func (*Refusal) Error

func (r *Refusal) Error() string

type Snapshot

type Snapshot struct {
	// Order is the configured order, its Status and Kind values the
	// board's canonical names.
	Order []config.BoardStep
	// Items are the open board items in the board's order.
	Items []Item
	// Search holds each search step's issues by its index in Order.
	Search map[int][]Item
	// Statuses are the board's Status values, in the board's order.
	Statuses []string
	// Gate is the epic gate; Plans the plans of the epics it watches by
	// their lower-cased ref.
	Gate  Gate
	Plans map[string]*Plan
	// Missing names what GitHub answered with an error and no item's
	// Missing holds ("<path>: <message>"), a search step's issues for one.
	Missing []string
}

Snapshot is what one read of the board returned.

func (*Snapshot) Epics added in v0.127.0

func (snap *Snapshot) Epics() []Epic

Epics are the snapshot's epics in planning or past their go, in the board's order, each with its plan.

func (*Snapshot) Parent added in v0.131.0

func (snap *Snapshot) Parent(ref string) string

Parent is the parent of the issue ref among the items snap read, the board items and the sub-issues read with them; "" when snap read none.

type Thread added in v0.142.0

type Thread struct {
	About string `json:"about"`
	Ref   string `json:"thread"`
	Via   string `json:"via,omitempty"`
	// contains filtered or unexported fields
}

Thread is where an answer about an issue or pull request is recorded: Ref itself in a private repository; else the first private plan pull request naming it or an epic above it, then the nearest epic above it in a private repository. Via says why Ref is not About, "" when it is.

Jump to

Keyboard shortcuts

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