timeline

package
v0.0.8 Latest Latest
Warning

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

Go to latest
Published: Jul 3, 2026 License: MIT Imports: 5 Imported by: 0

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

View Source
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

func BuildJobLabel(j *JobInfo) string

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

func FormatElapsed(t time.Time) string

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

func JobDuration(j *JobInfo) string

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 EventKind

type EventKind string

EventKind distinguishes action rows from job rows.

const (
	KindAction EventKind = "action"
	KindJob    EventKind = "job"
)

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

type StatusGroup struct {
	Status       string
	EnteredAt    time.Time
	HasEnteredAt bool
	Events       []Event
}

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

Jump to

Keyboard shortcuts

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