Documentation
¶
Overview ¶
Package lookaround collects facts about one project and derives what changed in a window (the feed) or where each of its tasks stands (the state). Collectors (host, tracker, local) fetch and interpret nothing; DeriveFeed and Derive are pure functions, facts in, rows out, and the only places a group or a stage is decided; the Board is the JSON every renderer reads, docs/board.md its spec and docs/board.schema.json its schema.
Index ¶
- Constants
- Variables
- func AgentFor(id, branch, path string) string
- func Plural(n int, word string) string
- func StateGroup(r Row) string
- func StateRank(r Row) int
- type Board
- type Branch
- type Facts
- type Feed
- type FeedRow
- type Findings
- type Inputs
- type Local
- type LocalInputs
- type Next
- type PR
- type PRRef
- type Person
- type Project
- type Quiet
- type RelatedPR
- type ReviewFacts
- type Row
- type Source
- type Task
- type Tip
- type Where
- type Who
- type Window
- type Workspace
- type Worktree
Constants ¶
const ( ViewFeed = "feed" ViewState = "state" )
The two views a board is: what changed in the window, and where every task stands.
const ( SourceHost = "host" SourceTracker = "tracker" SourceLocal = "local" )
The three sources, in the order the board lists them.
const ( StageAsksWaiting = "asks-waiting" StageBlocked = "blocked" StageReady = "ready" StageInReview = "in-review" StagePROpen = "pr-open" StageImplementing = "implementing" StagePlanning = "planning" StagePlanned = "planned" StageMergedUnfinished = "merged-unfinished" StageNext = "next" StageDone = "done" )
The stages, in the order the board lists rows.
const ( GroupInProgress = "in progress" GroupNext = "next" GroupDone = "done" )
The groups the state's rows draw under, in listing order.
const ( FromLocal = "local" FromTrace = "trace" FromHost = "host" FromTracker = "tracker" FromGuessed = "guessed" )
Where a stage's or a group's evidence came from: local state, the traces f10 left on the host, the host's certain facts (a merge, a creation), the tracker's list, or the host's facts read as a guess.
const ( GroupStarted = "started" GroupUpdated = "updated" GroupReviewed = "reviewed" GroupFinished = "finished" GroupNewTasks = "new-tasks" )
The feed's groups, in the order it lists them.
const ( RelationPart = "part" // the two overlapped RelationFollowUp = "follow-up" // it merged before the later one opened RelationReplaced = "replaced" // it closed unmerged )
How a task's earlier request relates to the one its feed row leads with.
const Version = 4
Version is the Board JSON's version; a change to a stage rule or a field's meaning bumps it with the spec.
Variables ¶
var Groups = []string{GroupStarted, GroupUpdated, GroupReviewed, GroupFinished, GroupNewTasks}
Groups is every feed group in listing order.
var Stages = []string{ StageAsksWaiting, StageBlocked, StageReady, StageInReview, StagePROpen, StageImplementing, StagePlanning, StagePlanned, StageMergedUnfinished, StageNext, StageDone, }
Stages is every stage in listing order.
var StateGroups = []string{GroupInProgress, GroupNext, GroupDone}
StateGroups is every state group in listing order.
Functions ¶
func AgentFor ¶
AgentFor is the name start gave the task's agent in the worktree: the id and the suffix its branch carries, hashed with the path.
func Plural ¶ added in v0.39.0
Plural is n and word, the word in its plural past one: "-sh" takes "es" ("2 force pushes"). Every renderer counting a word goes through it.
func StateGroup ¶ added in v0.45.0
StateGroup is the heading a state row draws under: a row held only by its base, with no work of its own, is still next. board.html's group() mirrors it.
Types ¶
type Board ¶
type Board struct {
Version int `json:"version"`
GeneratedAt time.Time `json:"generated_at"`
View string `json:"view"`
Project Project `json:"project"`
Window Window `json:"window"`
Sources []Source `json:"sources"`
Rows []Row `json:"rows"` // the state view's; empty in the feed
Feed *Feed `json:"feed,omitempty"` // the feed view's
}
A Board is one project's feed or rows, with where each fact came from.
func Derive ¶
Derive turns facts into the board: one row per task or taskless request, its stage decided by the rules docs/board.md lists in order, the first that matches winning, and each rule naming where its evidence came from. It reads no clock and does no I/O: now and since are the board's.
func DeriveFeed ¶ added in v0.39.0
DeriveFeed turns facts into the feed over the window: one row per task whose requests had activity there, one per such request naming no task, each in the one group its activity reads as by the rules docs/board.md lists, one per task created there, and the open requests with none counted in the quiet line. Like Derive it reads no clock and does no I/O.
func Unreachable ¶
Unreachable is the board of a project no collector could run for: a checkout whose instructions do not read, or a repository with no host CLI. Every source carries the error, so a reader of several boards sees the project failed rather than empty; a project with no checkout keeps its local source skipped, as a remote read does.
type Branch ¶
type Branch struct {
Task string // the id the branch is linked to, else what its name spells
Riders []string // a bundle's other members
After string // the base task recorded on it
Model string // the agent's model, where start recorded it
// Finish is `f10 drive`'s record of running the branch's finish, when
// that drive still runs
Finish *ref.DriveFinish
}
A Branch is what the local config records on a branch.
type Facts ¶
type Facts struct {
Project Project
Prefix string // the task id prefix, for reading ids off branches and titles
Tasks []Task
PRs []PR
Local *Local // nil when the local collector was skipped or failed
People []Person
Sources []Source
// the feed's: what the host did in the window, each started task's
// other requests by id, and a local-only checkout's branch tips
Activity *forge.Activity
Earlier map[string][]forge.PR
Tips []Tip
}
Facts is what the collectors found, before any interpretation.
type Feed ¶ added in v0.39.0
A Feed is what changed in the window: one row per task whose requests were touched there, per touched request naming no task, and per task created there, and the open requests nobody touched, counted.
type FeedRow ¶ added in v0.39.0
type FeedRow struct {
Group string `json:"group"`
Key string `json:"key"` // the task id, else `#<number>`, else the branch
Task *Task `json:"task,omitempty"`
PR *PRRef `json:"pr,omitempty"`
From string `json:"from"`
Who Who `json:"who"`
// Owes is who the next move waits on: the author after a review, a
// requested reviewer before one.
Owes string `json:"owes,omitempty"`
Abandoned bool `json:"abandoned,omitempty"` // closed without merging, nothing later
Related []RelatedPR `json:"related,omitempty"` // the task's requests opened before PR
Summary string `json:"summary,omitempty"` // what happened, one sentence
At time.Time `json:"at"` // the latest event in the window
}
A FeedRow is one task whose requests were touched in the window, one request naming no task, or one task created there.
type Findings ¶
type Findings struct {
Round int
Open int // findings with no verdict, or a verdict of ask
At time.Time
}
Findings is a task's latest review round as its findings file holds it.
type Inputs ¶
type Inputs struct {
Project Project
Prefix string
// Since is where reading starts: the state view's done window, or the
// earliest span the feed may show.
Since time.Time
// Feed reads what changed since Since instead of where every task
// stands: the host's activity, the tracker's list, and nothing local
// unless the checkout is local only.
Feed bool
// LocalOnly is a checkout with no origin remote: the host is skipped,
// and the tracker too unless a driver answers it (Tasks set).
LocalOnly bool
Host *forge.Host
HostError string // why there is no host, for the source's error
Tasks func(ctx context.Context) ([]Task, error)
// RemoteOpen reports whether the project's declared remote reviewer
// left a review on the request nobody handled; nil declares none. An
// error is a note on the host source, the request kept without it.
RemoteOpen func(ctx context.Context, number int) (bool, error)
Remote bool
Local *LocalInputs
// People is the user's map of one person's identities across the
// sources; nil groups rows by whatever identity each source gives.
People []Person
// Now is the clock; nil is time.Now. A test fixes it so timings are
// stable.
Now func() time.Time
}
Inputs binds the collectors to one project. A nil Host, Tasks or Local is that source absent; Remote skips the local collector on purpose.
type Local ¶
type Local struct {
Runs []state.Run
Worktrees []Worktree
Branches map[string]Branch // by branch name
Plans map[string]time.Time // task id to the plan file's mtime
Archived map[string]bool // task ids whose plan is archived
Findings map[string]Findings // task id to its latest round
Verdicts map[string]string // task id to its latest verdict
Finished map[string]bool // task ids the ledger holds task_finished for
Workspaces map[string]Workspace // worktree path to the Herdr workspace on it
}
Local is what this machine knows: live runs, worktrees, branches, plans, findings, verdicts, what the ledger closed, and Herdr's view.
type LocalInputs ¶
type LocalInputs struct {
Main string // the main checkout
StateDir string // run state files
TTL time.Duration
LedgerPath string // "" reads no ledger
ProjectID string // the ledger's project id
Herdr bool // Herdr is reachable: read its workspaces
}
LocalInputs is where this machine keeps what the local collector reads.
type PR ¶
type PR struct {
forge.PR
Block *trace.State `json:"block,omitempty"`
Trailers []trace.Parsed `json:"trailers,omitempty"`
HeadExists *bool `json:"head_exists,omitempty"` // read for merged requests only
RemoteOpen bool `json:"remote_open,omitempty"` // the declared remote reviewer left one unhandled
}
A PR is a request with what the host says around it.
type PRRef ¶
type PRRef struct {
Number int `json:"number"`
Title string `json:"title,omitempty"`
URL string `json:"url,omitempty"`
State string `json:"state"` // open, merged or closed
Draft bool `json:"draft,omitempty"`
Head string `json:"head,omitempty"`
Base string `json:"base,omitempty"`
Mergeable string `json:"mergeable,omitempty"` // clean, conflicts or blocked
Checks string `json:"checks,omitempty"` // green, red or pending
}
A PRRef is the request a row hangs on.
type Person ¶
A Person joins the identities one person has across the sources: the tracker's name for them, their host login, the emails their commits carry. Every field but Name is optional.
type Project ¶
type Project struct {
ID string `json:"id"` // the normalised remote, `github.com/org/repo`
Name string `json:"name"` // the main checkout's basename
Host string `json:"host,omitempty"` // gh or glab
Checkout string `json:"checkout,omitempty"` // the main checkout; empty with no checkout read
Remote bool `json:"remote"` // local collectors skipped
// LocalOnly is a checkout with no origin remote: no host to read, so
// the board is this machine's alone.
LocalOnly bool `json:"local_only,omitempty"`
}
Project names the project the board is about.
type Quiet ¶ added in v0.39.0
type Quiet struct {
Count int `json:"count"`
Oldest time.Time `json:"oldest,omitzero"` // the oldest one's creation time
}
Quiet is the open requests with no row in the window: no commits, reviews or status changes there.
type RelatedPR ¶ added in v0.44.0
type RelatedPR struct {
Number int `json:"number"`
URL string `json:"url,omitempty"`
State string `json:"state"` // open, merged or closed
Relation string `json:"relation"`
}
A RelatedPR is one of a task's earlier requests, beside the row's own.
type ReviewFacts ¶
type ReviewFacts struct {
Step string `json:"step,omitempty"` // the step the state block names
Round int `json:"round,omitempty"`
Open int `json:"open,omitempty"`
Asks int `json:"asks,omitempty"`
Approvals int `json:"approvals,omitempty"`
ChangesRequested int `json:"changes_requested,omitempty"`
RemoteOpen bool `json:"remote_open,omitempty"` // the declared remote reviewer left one unhandled
}
ReviewFacts is what the row's reviews add up to.
type Row ¶
type Row struct {
Key string `json:"key"` // the task id, else `#<number>`
Task *Task `json:"task,omitempty"`
Stage string `json:"stage"`
StageFrom string `json:"stage_from"`
Who Who `json:"who"`
Where Where `json:"where"`
Review *ReviewFacts `json:"review,omitempty"`
BlockedBy []string `json:"blocked_by,omitempty"`
LastActivity time.Time `json:"last_activity,omitzero"`
Next Next `json:"next"`
}
A Row is one task, or one request naming no task.
type Source ¶
type Source struct {
Name string `json:"name"`
OK bool `json:"ok"`
Skipped bool `json:"skipped,omitempty"`
Error string `json:"error,omitempty"`
Notes []string `json:"notes,omitempty"`
TookMS int64 `json:"took_ms"`
}
A Source is one collector's result: ok with its facts in, failed with its error and nothing from it, or skipped on purpose.
type Task ¶
type Task struct {
ID string `json:"id"`
Title string `json:"title,omitempty"`
URL string `json:"url,omitempty"`
Status string `json:"status,omitempty"`
Priority string `json:"priority,omitempty"`
Assignee string `json:"assignee,omitempty"`
After string `json:"after,omitempty"` // the base its body names
// Created is when the tracker created it, where the tracker says.
Created time.Time `json:"created,omitzero"`
}
A Task is a tracker row.
type Tip ¶ added in v0.39.0
A Tip is a local branch's last commit, what a checkout with no host shows in its feed.
type Where ¶
type Where struct {
PR *PRRef `json:"pr,omitempty"`
Branch string `json:"branch,omitempty"`
Worktree string `json:"worktree,omitempty"`
Workspace string `json:"workspace,omitempty"`
// BranchLeft marks a merged row whose branch outlived its worktree
BranchLeft bool `json:"branch_left,omitempty"`
}
Where the work is.
type Who ¶
type Who struct {
Agent string `json:"agent,omitempty"` // the Herdr agent in its worktree
AgentStatus string `json:"agent_status,omitempty"` // idle, working, done
Model string `json:"model,omitempty"`
Author string `json:"author,omitempty"` // the request's author
Assignees []string `json:"assignees,omitempty"`
Reviewers []string `json:"reviewers,omitempty"` // requested and not yet heard from
Committers []string `json:"committers,omitempty"` // the author emails on the row's commits
// Person is the one name the row groups under: the tracker assignee's,
// else the author's, else the first committer's, each through the
// people map where one matches, else as the source spelled it.
Person string `json:"person,omitempty"`
}
Who is working on the row, and who it waits on.
type Window ¶
type Window struct {
Since time.Time `json:"since"`
// End closes the feed's span, exclusive; zero in the state view.
End time.Time `json:"end,omitzero"`
// Chosen is how the feed's span was picked: the window named
// (`today`, `last-week`, a date) or the default's pick.
Chosen string `json:"chosen,omitempty"`
// Fallback is the default falling back to the last working day
// because today has no activity yet.
Fallback bool `json:"fallback,omitempty"`
}
Window is the period the board covers: the feed's span, or the time since which the state view's done rows count.
func ChooseWindow ¶ added in v0.39.0
ChooseWindow is the default's span: today when the facts give today's feed a row, else the last working day, marked as the fallback. Activity that makes no row (a review comment) keeps the fallback. The facts are read from the last working day's start, so one collection answers both.