Documentation
¶
Index ¶
- Constants
- Variables
- func CheckLearningsCap(tickDir string) (lines int, over bool, err error)
- func HandleClose(t *Tick, reason string) (routed bool)
- func ProcessVerdict(t *Tick) (closed bool, err error)
- type Activity
- type IDGenerator
- type Store
- func (s *Store) Delete(id string) error
- func (s *Store) Ensure() error
- func (s *Store) LastActivityForTick(tickID string) (*time.Time, error)
- func (s *Store) LastActivityForTicks(ids []string) (map[string]*time.Time, error)
- func (s *Store) List() ([]Tick, error)
- func (s *Store) LogActivity(tickID, action, actor, epic string, data map[string]interface{}) error
- func (s *Store) Read(id string) (Tick, error)
- func (s *Store) ReadActivity(limit int) ([]Activity, error)
- func (s *Store) Write(t Tick) error
- func (s *Store) WriteAs(t Tick, actor string) error
- type Tick
- func (t *Tick) ClearAwaiting()
- func (t *Tick) GetAwaitingType() string
- func (t *Tick) HasRequiredGate() bool
- func (t *Tick) IsAwaitingHuman() bool
- func (t *Tick) IsTerminalAwaiting() bool
- func (t *Tick) Release()
- func (t *Tick) SetAwaiting(value string)
- func (t *Tick) Start()
- func (t Tick) Validate() error
Constants ¶
const ( ActivityCreate = "create" ActivityUpdate = "update" ActivityClose = "close" ActivityReopen = "reopen" ActivityNote = "note" ActivityApprove = "approve" ActivityReject = "reject" ActivityBlock = "block" ActivityUnblock = "unblock" ActivityAssign = "assign" ActivityAwaiting = "awaiting" ActivityStart = "start" // logged when task is claimed by pool worker ActivityStaleRecovery = "stale_recovery" // logged when stale task is reset )
Activity actions
const ( StatusOpen = "open" StatusInProgress = "in_progress" StatusClosed = "closed" )
Status values.
const ( TypeBug = "bug" TypeFeature = "feature" TypeTask = "task" TypeEpic = "epic" TypeChore = "chore" )
Type values.
const ( RequiresApproval = "approval" RequiresReview = "review" RequiresContent = "content" )
Requires values (pre-declared gates).
const ( AwaitingWork = "work" AwaitingApproval = "approval" AwaitingInput = "input" AwaitingReview = "review" AwaitingContent = "content" AwaitingEscalation = "escalation" AwaitingCheckpoint = "checkpoint" )
Awaiting values (current wait state).
const ( VerdictApproved = "approved" VerdictRejected = "rejected" )
Verdict values (human response to awaiting state).
const ( RoleReview = "review" RoleCloseout = "closeout" )
Role values (process ticks in an epic's EPIC-SKELETON). A runnable epic ends with two process ticks: a final-review tick (role=review) blocked by every last-wave implementation tick, and a close-out tick (role=closeout) blocked by the final-review tick. The role is structural so orchestrators can detect a missing skeleton mechanically (tk graph --json → missing_process_ticks) instead of matching tick titles.
const LearningsCap = 150
LearningsCap is the maximum recommended line count for .tick/learnings.md.
const TargetDateLayout = "2006-01-02"
TargetDateLayout is the canonical format for the optional TargetDate field: a precise ISO calendar day with no time-of-day component.
Variables ¶
var ( ValidRequiresValues = []string{RequiresApproval, RequiresReview, RequiresContent} ValidAwaitingValues = []string{AwaitingWork, AwaitingApproval, AwaitingInput, AwaitingReview, AwaitingContent, AwaitingEscalation, AwaitingCheckpoint} ValidVerdictValues = []string{VerdictApproved, VerdictRejected} ValidRoleValues = []string{RoleReview, RoleCloseout} )
Valid values for workflow fields (for validation and documentation).
Functions ¶
func CheckLearningsCap ¶ added in v0.13.0
CheckLearningsCap counts the lines in <tickDir>/learnings.md and reports whether the file exceeds LearningsCap.
If the file does not exist, (0, false, nil) is returned. An empty file is treated as 0 lines (over = false). Lint errors (unexpected OS errors) are swallowed: (0, false, nil).
func HandleClose ¶ added in v0.1.18
HandleClose processes a close request for a tick. If the tick has a required gate (Requires field set), it routes to human instead of closing. Returns (routed bool) indicating whether the tick was routed to human instead of closed.
When routed:
- Sets Awaiting = Requires
- Adds note explaining work is complete, awaiting specified review
- Does NOT close the tick
When closing:
- Sets Status = closed
- Sets ClosedAt = now
- Sets ClosedReason = reason
The Requires field persists, so if human rejects and agent completes again, it will route to human again.
func ProcessVerdict ¶ added in v0.1.18
ProcessVerdict processes a verdict on an awaiting tick and returns whether the tick was closed. This is the core state machine for agent-human workflow.
Logic:
- Terminal states (work, approval, review, content): close on approved
- Non-terminal states (input, escalation): close on rejected (can't/won't proceed)
- Checkpoint: never closes, always returns to agent
After processing, Awaiting and Verdict are cleared regardless of outcome.
Types ¶
type Activity ¶ added in v0.2.1
type Activity struct {
Timestamp time.Time `json:"ts"`
TickID string `json:"tick"`
Action string `json:"action"`
Actor string `json:"actor"`
Epic string `json:"epic,omitempty"`
Data map[string]interface{} `json:"data,omitempty"`
}
Activity represents a single activity log entry.
type IDGenerator ¶
type IDGenerator struct {
// contains filtered or unexported fields
}
IDGenerator produces random base36 tick IDs.
func NewIDGenerator ¶
func NewIDGenerator(rng *rand.Rand) *IDGenerator
NewIDGenerator returns a generator. If rng is nil, a time-based source is used.
type Store ¶
type Store struct {
Root string
}
Store handles tick file persistence.
func (*Store) LastActivityForTick ¶ added in v0.13.0
LastActivityForTick returns the maximum activity timestamp for a single tick ID, or nil if the log is missing or has no valid entries for the tick. Implemented via LastActivityForTicks.
func (*Store) LastActivityForTicks ¶ added in v0.13.0
LastActivityForTicks scans activity.jsonl once and returns the maximum timestamp found for each of the requested tick IDs. IDs with no (valid) entries are absent from the result map. A missing log file yields an empty map and no error; malformed lines are skipped. Errors are returned only for genuine I/O failures.
func (*Store) LogActivity ¶ added in v0.2.1
LogActivity appends an activity entry to the activity log.
func (*Store) ReadActivity ¶ added in v0.2.1
ReadActivity reads the last N activity entries.
type Tick ¶
type Tick struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
Notes string `json:"notes,omitempty"`
Status string `json:"status"`
Priority int `json:"priority"`
Type string `json:"type"`
Owner string `json:"owner"`
Labels []string `json:"labels,omitempty"`
BlockedBy []string `json:"blocked_by,omitempty"`
// After expresses preferred ordering only (work these targets first if
// feasible); it never gates readiness — that is BlockedBy. Entries are
// tick IDs; consumers ignore missing or closed targets.
After []string `json:"after,omitempty"`
Parent string `json:"parent,omitempty"`
// TargetDate is an optional precise ISO calendar day (e.g. "2026-09-30"):
// no time-of-day, no timezone, no fuzziness. Absent (empty) is the common
// case. It feeds the derived overdue / on-track signal (later ticks) and
// never gates execution.
TargetDate string `json:"target_date,omitempty"`
// Role marks a process tick in an epic's EPIC-SKELETON: "review" for the
// final-review tick, "closeout" for the close-out/retro tick. Empty (the
// common case) means a normal work tick. Structural, not title-derived —
// tk graph --json reports missing_process_ticks from this field.
Role string `json:"role,omitempty"`
DiscoveredFrom string `json:"discovered_from,omitempty"`
AcceptanceCriteria string `json:"acceptance_criteria,omitempty"`
DeferUntil *time.Time `json:"defer_until,omitempty"`
ExternalRef string `json:"external_ref,omitempty"`
Manual bool `json:"manual,omitempty"`
BaseBranch string `json:"base_branch,omitempty"`
Requires *string `json:"requires,omitempty"`
Awaiting *string `json:"awaiting,omitempty"`
Verdict *string `json:"verdict,omitempty"`
CreatedBy string `json:"created_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
StartedAt *time.Time `json:"started_at,omitempty"`
ClosedAt *time.Time `json:"closed_at,omitempty"`
ClosedReason string `json:"closed_reason,omitempty"`
}
Tick represents a single work item on disk.
func (*Tick) ClearAwaiting ¶ added in v0.1.18
func (t *Tick) ClearAwaiting()
ClearAwaiting clears the awaiting state and the legacy Manual field.
func (*Tick) GetAwaitingType ¶ added in v0.1.18
GetAwaitingType returns the awaiting type, handling backwards compatibility with Manual. Returns empty string if not awaiting human action.
func (*Tick) HasRequiredGate ¶ added in v0.1.18
HasRequiredGate returns true if tick has a pre-declared approval gate.
func (*Tick) IsAwaitingHuman ¶ added in v0.1.18
IsAwaitingHuman returns true if tick is waiting for human action. This includes ticks with Awaiting set or legacy Manual flag.
func (*Tick) IsTerminalAwaiting ¶ added in v0.1.18
IsTerminalAwaiting returns true if approved verdict should close the tick. Terminal awaiting types: approval, review, content, work Non-terminal awaiting types: input, escalation, checkpoint
func (*Tick) Release ¶ added in v0.10.0
func (t *Tick) Release()
Release transitions the tick back to open status, clearing the started timestamp. Used when work on a tick is abandoned or failed and needs to be picked up again. Sets Status=open, StartedAt=nil, UpdatedAt=now.
func (*Tick) SetAwaiting ¶ added in v0.1.18
SetAwaiting sets the awaiting state and clears the legacy Manual field. Pass empty string to clear the awaiting state. This ensures migration from Manual to Awaiting field.