Documentation
¶
Overview ¶
Package timeline builds the unified task-detail timeline consumed by both the TUI and the Web UI. It imports orchestrator only — no api dependency — so web/templates can import it without creating an import cycle with internal/api (which pulls in web/templates for rendering).
The builder takes fully-resolved inputs: the task, its actions, and a list of JobInfo records. Each caller adapts from its own Job shape (api.Job / dispatcher job model) via ConvertAPIJob-style helpers.
Index ¶
- Constants
- func BuildActionLabel(a *orchestrator.Action) string
- func BuildJobLabel(j *JobInfo) string
- func FormatElapsed(t time.Time) string
- func IsProgressAction(a *orchestrator.Action) bool
- func IsStateTransition(a *orchestrator.Action) bool
- func JobDuration(j *JobInfo) string
- type Event
- type EventKind
- type JobInfo
- type StatusGroup
Constants ¶
const ( JobStatusRunning = "running" JobStatusCompleted = "completed" JobStatusFailed = "failed" )
JobStatus mirrors the string values used by api.JobStatus so timeline renderers don't need to import api to discriminate running / completed / failed states. Callers pass these strings through.
Variables ¶
This section is empty.
Functions ¶
func BuildActionLabel ¶
func BuildActionLabel(a *orchestrator.Action) string
BuildActionLabel returns the display label for a timeline action. State transitions: "<type> → <to_status>". Progress actions: "進捗: <message>" (extracted from JSON payload).
func BuildJobLabel ¶
BuildJobLabel returns the display label for a job.
- completed: "[role] <name> ✓ <duration>" (name omitted when empty)
- failed: "[role] <name> ✗ <duration>"
- running: "[role] <elapsed> ago"
- other: "[role] <name><status>"
DisplayName is used when set; otherwise HandlerID is used as fallback. When role is "hook", the "[hook]" prefix is omitted — the handler name alone identifies the hook sufficiently.
func FormatElapsed ¶
FormatElapsed returns a short MM:SS (or HH:MM:SS) elapsed string since t. Matches the TUI helper so that TUI display is unchanged after shared-timeline migration. Exported so the Web UI can compute the initial server-side value for a running job's live-ticking elapsed counter.
func IsProgressAction ¶
func IsProgressAction(a *orchestrator.Action) bool
IsProgressAction reports whether an action is a non-transitioning progress note.
func IsStateTransition ¶
func IsStateTransition(a *orchestrator.Action) bool
IsStateTransition reports whether an action moves the task to a different status.
func JobDuration ¶
JobDuration returns a human-readable duration for a completed/failed job. Returns "?" when the job has no UpdatedAt or UpdatedAt is not after CreatedAt.
Types ¶
type Event ¶
type Event struct {
Time time.Time
HasTime bool
Kind EventKind
Label string
Action *orchestrator.Action
Job *JobInfo
Sticky bool
}
Event is a single row in the unified timeline. Exactly one of Action / Job is populated, matching Kind.
Sticky marks a synthesized reference row that surfaces a long-lived job under the task's CURRENT status group (in addition to the historic group where the job originally landed). Without this, a single hook job that outlives multiple ask/answer round trips (the canonical `boid task ask` blocking RPC pattern) would only render under its starting executing group while later status visits look empty even though the same agent process is still running there. Sticky events are non-authoritative — they share the underlying *JobInfo with the original row — so renderers must treat them as display-only (no separate progress fetch, no double-counted runtime, etc.).
func SelectableEvents ¶
func SelectableEvents(groups []StatusGroup) []Event
SelectableEvents returns the flat event list from all groups in order. Used by TUI for cursor clamping and enter-key drilldown.
type JobInfo ¶
type JobInfo struct {
ID string
Role string
HandlerID string
DisplayName string // optional; shown instead of HandlerID when non-empty
Status string // one of JobStatusRunning / Completed / Failed (or other)
ExitCode int
CreatedAt time.Time
UpdatedAt time.Time
}
JobInfo is the minimum job data needed to place a job on the timeline and render its label / status icon / link target. Callers convert from their native job type (api.Job for TUI / Web).
type StatusGroup ¶
StatusGroup groups events under a single task-status visit. Repeated visits to the same status produce distinct groups in order.
func Build ¶
func Build(task *orchestrator.Task, actions []*orchestrator.Action, jobs []*JobInfo) []StatusGroup
Build groups filtered events by the task-status visit in which they occurred. Only state-transition actions and jobs are included. hook_fired actions are intentionally dropped because the associated job carries the same information (success, handler id, duration) plus output.
Each visit to a status creates a new group so repeated visits (e.g. executing → aborted → pending → executing) produce distinct groups in chronological order instead of collapsing same-status events into one.
Each group's EnteredAt records when the task entered that visit:
- initial group: task.CreatedAt
- subsequent groups: the CreatedAt of the transition action that moved into it