activity

package
v2.11.0 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: 13 Imported by: 0

Documentation

Overview

Package activity is the port through which what people do in an interface is recorded (docs/adr/0034), so their work can be judged the way agents' calls are (docs/EVALUATING_PEOPLE.md): where a task goes wrong, where it waits, where it takes more than it needs. An act is recorded by its shape (the kind, the record, the step, the field's path, the action, its outcome and how long it took), never by what was written: no text a person typed, no name and no figure reaches a trace. An adapter keeps the acts; "off", the default, keeps none.

Index

Constants

View Source
const (
	Server    = "server"    // the engine decided it, for any interface
	Interface = "interface" // only the interface saw it
)

Sources of an act.

View Source
const (
	OK      = "ok"
	Refused = "refused" // the engine said no and why
	Failed  = "failed"  // the request could not be answered
)

Outcomes of an act.

View Source
const (
	DraftSave       = "draft.save"       // a draft kept, in a change set or the working copy
	DraftDiscard    = "draft.discard"    // a draft thrown away
	VersionSave     = "version.save"     // Save as version, ok or refused
	CheckLeave      = "check.leave"      // a check left open, with a reason
	ChangeSetStart  = "changeset.start"  // a change set opened
	ChangeSetSubmit = "changeset.submit" // a change set sent for review
	ChangeSetAccept = "changeset.accept" // a change set rolled in, ok or refused
	ChangeSetClose  = "changeset.close"  // a change set closed without rolling in
	ChangeSetReopen = "changeset.reopen" // a closed change set opened again
)

Names of the acts the server records on a person's behalf.

View Source
const (
	ScreenShow      = "screen.show"      // a screen drawn; Millis until its content, Sign whether a wait showed
	ScreenDeadEnd   = "screen.deadend"   // a screen with nothing to do: an empty state or an error with no action
	FlowOpen        = "flow.open"        // a flow opened on a record; Target new or existing
	StepEnter       = "step.enter"       // a step of a flow entered
	StepBack        = "step.back"        // a step left backwards
	FieldSet        = "field.set"        // a field's answer given (on change, never per key)
	GuideOpen       = "guide.open"       // a field's guide or examples opened
	PickerOpen      = "picker.open"      // a menu, popover or picker opened
	PickerClose     = "picker.close"     // closed; Target chosen or none
	Press           = "press"            // a press; Target action or none; Millis until the next frame
	Request         = "request"          // a request to the server; Millis, Outcome, Sign
	FindOpen        = "find.open"        // the search opened
	FindPick        = "find.pick"        // a record picked from it
	FindClose       = "find.close"       // closed with nothing picked
	ChangeSetSwitch = "changeset.switch" // another change set made the one worked in
)

Names of the acts only an interface sees.

View Source
const (
	Define = "define" // a record taken through its flow to a version
	Change = "change" // a record that had a version, saved again
	Review = "review" // a change set from opened to rolled in or closed
	Find   = "find"   // a record searched for and picked
)

Task types.

View Source
const (
	Finished = "finished"
	GivenUp  = "given-up"
	Open     = "open" // still being worked on when the trace ends
)

Task statuses.

View Source
const (
	PressBudget   = 100 * time.Millisecond
	RequestBudget = time.Second
	// RepeatWithin is how close three presses on one thing must be to
	// count as pressing again because nothing seemed to happen.
	RepeatWithin = time.Second
)

The answers' budgets, from DESIGN_RULES.md ("The interface answers").

View Source
const (
	ValueAdding = "value-adding" // an answer kept, a version saved
	Necessary   = "necessary"    // moving through what the task needs
	Waste       = "waste"        // anything in the eight wastes
)

Value classes of an act, as a value stream counts them.

View Source
const (
	Day   = "day"
	Week  = "week"
	Build = "build"
)

Periods a reading can be taken over.

Variables

View Source
var NotMeasured = []string{
	"defects: a version undone",
	"overproduction: a record defined twice",
	"overproduction: a record left alone",
	"non-used knowledge: typed again",
	"non-used knowledge: a reference made by hand",
	"extra processing: a diff with noise",
	"extra processing: a review of nothing",
}

NotMeasured are the document's criteria this analysis does not count yet, said in the report so a reader never takes a missing figure for a clean one.

Functions

func Budget

func Budget() (map[string]int, error)

Budget is the committed budget of each task's shortest path, by "type/Kind": what Paths may not exceed without the budget changing in the same change, and what cartograph ux reads extra interactions against.

func CheckBatch

func CheckBatch(session, iface string) error

CheckBatch says why a batch's session or interface is refused, or nil: both are tokens, never a name.

func FlowKinds

func FlowKinds(flows map[string]Flow) []string

FlowKinds lists the kinds of flows, in order.

func Flows

func Flows() (map[string]Flow, error)

Flows reads every kind's flow from the contract, by kind.

func IsOff

func IsOff(r Recorder) bool

IsOff says whether r keeps nothing, so a caller can skip building acts.

func Paths

func Paths(flows map[string]Flow) (map[string]int, error)

Paths works out every task's shortest path from the contract, by "type/Kind".

func PathsJSON

func PathsJSON(paths map[string]int) []byte

PathsJSON is the budget file's text for paths: sorted, one task a line, for a diff that reads at a glance.

func PeriodOf

func PeriodOf(period string, at time.Time, build string) string

PeriodOf is the period an instant or a build falls in: its date, its ISO week, or the build itself.

Types

type Bar

type Bar struct {
	Type string `json:"type"`
	Kind string `json:"kind,omitempty"`
	// MaxDefects is the most defects the task may meet: 0 means it must
	// pass first time. Absent, defects are not judged.
	MaxDefects *int `json:"maxDefects,omitempty"`
	// MaxSeconds is the longest the task may take, start to finish.
	MaxSeconds float64 `json:"maxSeconds,omitempty"`
	// MaxExtra is the most interactions the task may take, as a multiple
	// of its shortest path (paths.json).
	MaxExtra float64 `json:"maxExtra,omitempty"`
	// MaxSlowPresses is the largest share of presses answered after
	// 100 ms.
	MaxSlowPresses *float64 `json:"maxSlowPresses,omitempty"`
	// MaxDeadClicks is the most presses on nothing.
	MaxDeadClicks *int `json:"maxDeadClicks,omitempty"`
}

Bar is what a run must meet. Type and Kind name the task; the rest are limits, each checked only when set.

type Cause

type Cause struct {
	Kind  string `json:"kind"`
	Step  string `json:"step,omitempty"`
	Field string `json:"field"`
	Count int    `json:"count"`
}

Cause is one place defects fell: a field of a kind, in a step.

type Check

type Check struct {
	Name string `json:"name"`
	Pass bool   `json:"pass"`
	Why  string `json:"why"`
}

Check is one criterion of a run, judged.

func ScoreRun

func ScoreRun(r Report, b Bar) []Check

ScoreRun judges a run's report against the bar: the task must be finished, and each limit the bar sets met by the first such task.

type Criterion

type Criterion struct {
	Waste string  `json:"waste"`
	Name  string  `json:"name"`
	Count int     `json:"count"`
	Base  int     `json:"base"`
	Rate  float64 `json:"rate"`
	Per   string  `json:"per"`
}

Criterion is one waste criterion's figure: Count of Base, as a Rate per the unit Per names.

type Event

type Event struct {
	At     time.Time `json:"time"`
	Name   string    `json:"event.name"`
	Source string    `json:"cartograph.source"`
	// Session groups one person's acts in one window of an interface;
	// server acts have none and are joined to them by person and record.
	Session string `json:"session.id,omitempty"`
	// Build is the server's version, so readings can be taken per build.
	Build     string `json:"service.version,omitempty"`
	Person    string `json:"cartograph.person,omitempty"`
	Interface string `json:"cartograph.interface,omitempty"`
	// Surface is the screen, as its route pattern, never its address
	// with a record's name in it: goals/$id, not goals/feed-the-town.
	Surface   string `json:"cartograph.surface,omitempty"`
	Kind      string `json:"cartograph.kind,omitempty"`
	Record    string `json:"cartograph.record,omitempty"`
	Step      string `json:"cartograph.step,omitempty"`
	Field     string `json:"cartograph.field,omitempty"`
	ChangeSet string `json:"cartograph.change_set,omitempty"`
	// Target is what an act was on, from a short list: new, existing,
	// action, none, chosen.
	Target  string `json:"cartograph.target,omitempty"`
	Outcome string `json:"cartograph.outcome,omitempty"`
	Millis  int64  `json:"cartograph.duration.ms,omitempty"`
	// Sign is whether a wait showed it was waiting: a skeleton or a count.
	Sign bool `json:"cartograph.sign,omitempty"`
	// Problems counts what a refusal said is wrong; Paths are the fields
	// it named, list items as "-".
	Problems int      `json:"cartograph.problems,omitempty"`
	Paths    []string `json:"cartograph.problem.paths,omitempty"`
	// Checks are the ids of the checks still open on a version as it was
	// saved: what was left unmet, never what was written.
	Checks []string `json:"cartograph.checks.open,omitempty"`
}

Event is one act. Its names follow OpenTelemetry's general attributes where there is one (event.name, session.id, service.version); Cartograph's own are under cartograph.*.

func NewInterfaceAct

func NewInterfaceAct(e Event) (Event, error)

NewInterfaceAct is the one way an interface's act enters the trace: it comes from the interface, and every field holds a shape, or it is refused with the reason. An act that could carry what a person wrote cannot be made.

type Flow

type Flow struct {
	Kind  string
	Steps []string
	// StepOf is the step that asks each field, by its pointer.
	StepOf map[string]string
	// Fields is every field the flow asks, in order, with its step and
	// whether it is a list a person adds items to.
	Fields []FlowField
}

Flow is what the analysis needs of a kind's flow: its steps in order, which step asks each field, and its opportunities.

func (Flow) Opportunities

func (f Flow) Opportunities() int

Opportunities is how many places a Define or Change task on this kind can go wrong: each field the flow asks, and the decision to save it as a version. The same in every interface and every run, so defects per million compare across kinds.

func (Flow) StepFor

func (f Flow) StepFor(path string) string

StepFor is the step that asks the field a problem names, the longest field pointer that prefixes path; empty when no step asks it.

type FlowField

type FlowField struct {
	Path    string
	Step    string
	Control string
}

FlowField is one field a flow asks.

type LeadTime

type LeadTime struct {
	Name    string  `json:"name"`
	Count   int     `json:"count"`
	Median  float64 `json:"medianSeconds"`
	P95     float64 `json:"p95Seconds"`
	Average float64 `json:"averageSeconds"`
}

LeadTime is how long work waited, in seconds.

type Off

type Off struct{}

Off records nothing.

func (Off) Record

func (Off) Record(Event)

Record does nothing.

type Options

type Options struct {
	// Idle is how long a task may go untouched, before the trace ends,
	// and still count as open rather than given up. An hour when zero.
	Idle time.Duration
	// Budget is each task's shortest path, by "type/Kind" (see Paths).
	Budget map[string]int
}

Options tune the analysis.

type Query

type Query struct {
	From, To time.Time
	Person   string
}

Query says which acts to read: those from From (inclusive) to To (exclusive), either open when zero, and one person's when Person is set.

func (Query) Matches

func (q Query) Matches(e Event) bool

Matches says whether e is one of the acts q asks for.

type Reader

type Reader interface {
	Read(ctx context.Context, q Query) ([]Event, error)
}

Reader reads acts back for analysis, in the order they happened. It is the port every place a trace is kept answers, so the analysis never knows where that is.

type Recorder

type Recorder interface {
	Record(e Event)
}

Recorder keeps acts. Record never fails the act it records.

func Stamped

func Stamped(r Recorder, build string) Recorder

Stamped stamps every act with the server's build before keeping it.

type Report

type Report struct {
	Acts int       `json:"acts"`
	From time.Time `json:"from"`
	To   time.Time `json:"to"`
	// The whole: defects per million opportunities over Define and
	// Change tasks closed (finished or given up), and its sigma level.
	Opportunities  int     `json:"opportunities"`
	Defects        int     `json:"defects"`
	DPMO           float64 `json:"dpmo"`
	Sigma          float64 `json:"sigma"`
	FirstPassYield float64 `json:"firstPassYield"`
	Completion     float64 `json:"completion"`
	// ValueAddedRatio is the share of acts in tasks that changed the
	// record and were kept.
	ValueAddedRatio float64        `json:"valueAddedRatio"`
	Value           map[string]int `json:"value"`
	Tasks           []TaskStats    `json:"tasks"`
	Steps           []StepStats    `json:"steps,omitempty"`
	Waste           []Criterion    `json:"waste"`
	// CrossCutting are the figures for what bears on several sections at
	// once (risks, stances): raised where it bears, and placed when saved.
	CrossCutting []Criterion `json:"crossCutting"`
	LeadTimes    []LeadTime  `json:"leadTimes,omitempty"`
	Inventory    []Stock     `json:"inventory"`
	Pareto       []Cause     `json:"pareto,omitempty"`
	// NotMeasured are the criteria the document names that this build
	// does not count yet.
	NotMeasured []string `json:"notMeasured"`
	// Rebuilt are the tasks, for whoever reads further.
	Rebuilt []Task `json:"-"`
}

Report is the analysis of a set of acts.

func Analyse

func Analyse(acts []Event, flows map[string]Flow, o Options) Report

Analyse reads acts in the order they happened.

type Series

type Series struct {
	Name   string    `json:"name"`
	Counts []int     `json:"counts"`
	Chart  spc.Chart `json:"chart"`
	// Split is the first period the limits were worked out from, when
	// the readings were split at a release.
	Split string `json:"split,omitempty"`
}

Series is one figure read once a period, with each reading's count (the tasks or presses it was worked out from) and its XmR chart.

func Readings

func Readings(acts []Event, flows map[string]Flow, o Options, period, split string) []Series

Readings takes one reading per period of each figure that can be charted, and charts each series as an individuals and moving range chart (internal/spc). A task falls in the period it ended in; an act in the period it happened in. Periods are in the order they first appear, which for days and weeks is time order and for builds is the order they were served in. With split, the limits are worked out from that period on: a changed interface is a new process.

type StepStats

type StepStats struct {
	Kind   string  `json:"kind"`
	Step   string  `json:"step"`
	Tasks  int     `json:"tasks"`
	Passed int     `json:"passed"`
	Yield  float64 `json:"yield"`
}

StepStats is one step of a flow: how many tasks passed it first time, with no field it asks refused and no step back out of it.

type Stock

type Stock struct {
	Name          string  `json:"name"`
	Count         int     `json:"count"`
	MedianAgeSecs float64 `json:"medianAgeSeconds"`
}

Stock is work in progress at the end of the trace: how many, and how old in the median.

type Task

type Task struct {
	Type      string    `json:"type"`
	Status    string    `json:"status"`
	Person    string    `json:"person,omitempty"`
	Session   string    `json:"session,omitempty"`
	Kind      string    `json:"kind,omitempty"`
	Record    string    `json:"record,omitempty"`
	ChangeSet string    `json:"changeSet,omitempty"`
	Start     time.Time `json:"start"`
	End       time.Time `json:"end"`
	// Opportunities are the flow's fields and the decision to save.
	Opportunities int `json:"opportunities,omitempty"`
	// Defects are the problems refused versions named, and checks left
	// open.
	Defects int `json:"defects"`
	// Left are the checks left open, counted in Defects too.
	Left int `json:"left,omitempty"`
	// Refused are the fields refused versions named, with their steps.
	Refused []Cause `json:"refused,omitempty"`
	// Interactions are the presses and answers given while it was open.
	Interactions int `json:"interactions"`
	Screens      int `json:"screens"`
	Switches     int `json:"switches"`
	Backs        int `json:"backs"`
	Reworked     int `json:"reworked"`
	Hunting      int `json:"hunting"`
	Drafts       int `json:"drafts"`
	// OpenAtVersion are the checks left open on the version that
	// finished it.
	OpenAtVersion []string `json:"openAtVersion,omitempty"`
	// Build is the server build its last act was recorded on.
	Build string `json:"build,omitempty"`
	// Submitted is when a change set was sent for review.
	Submitted time.Time `json:"submitted,omitzero"`
	// contains filtered or unexported fields
}

Task is one piece of a person's work rebuilt from the trace.

func (Task) Seconds

func (t Task) Seconds() float64

Seconds is how long the task took, start to end.

type TaskBrief

type TaskBrief struct {
	// Name says what is judged.
	Name string `json:"name"`
	// Brief is what the person is told, word for word in every run.
	Brief string `json:"brief"`
	// Seed is a vault copied into each run's fresh vault before it is
	// served, for a task that starts from records; empty starts empty.
	Seed string `json:"seed,omitempty"`
	// Runs is how many passing runs in a row close it: three when zero.
	Runs int `json:"runs,omitempty"`
	Bar  Bar `json:"bar"`
}

TaskBrief is a task as data, kept beside the evaluation directory: what is asked, what finished means, and the bar a run must meet.

func ParseTaskBrief

func ParseTaskBrief(b []byte) (TaskBrief, error)

ParseTaskBrief reads a task from its JSON. Where the JSON comes from (a file beside an evaluation directory) is the caller's business: the domain reads no files.

type TaskStats

type TaskStats struct {
	Type           string  `json:"type"`
	Kind           string  `json:"kind,omitempty"`
	Started        int     `json:"started"`
	Finished       int     `json:"finished"`
	GivenUp        int     `json:"givenUp"`
	Open           int     `json:"open"`
	Completion     float64 `json:"completion"`
	FirstPassYield float64 `json:"firstPassYield"`
	// RolledThroughputYield is the product of each step's yield along the
	// kind's flow; Define and Change only.
	RolledThroughputYield float64 `json:"rolledThroughputYield,omitempty"`
	Opportunities         int     `json:"opportunities,omitempty"`
	Defects               int     `json:"defects"`
	DPMO                  float64 `json:"dpmo"`
	Sigma                 float64 `json:"sigma"`
	MedianSeconds         float64 `json:"medianSeconds"`
	P95Seconds            float64 `json:"p95Seconds"`
	MedianInteractions    float64 `json:"medianInteractions"`
	// ShortestPath is the budget's fewest interactions for the task, when
	// a budget was given; Extra the median ratio of interactions to it.
	ShortestPath int     `json:"shortestPath,omitempty"`
	Extra        float64 `json:"extra,omitempty"`
}

TaskStats is one task type on one kind.

Directories

Path Synopsis
Package conformance holds every place a people's trace is kept to the same promises: what is recorded is read back whole, in the order it happened, and a query keeps to its window and its person.
Package conformance holds every place a people's trace is kept to the same promises: what is recorded is read back whole, in the order it happened, and a query keeps to its window and its person.
Package jsonl keeps people's acts as JSON lines appended to a file, one act a line, for any tool that reads JSON lines (and for cartograph ux) to analyse.
Package jsonl keeps people's acts as JSON lines appended to a file, one act a line, for any tool that reads JSON lines (and for cartograph ux) to analyse.
Package memory keeps people's acts in the process, for tests and for a single process that analyses what it just recorded.
Package memory keeps people's acts in the process, for tests and for a single process that analyses what it just recorded.
Package postgres keeps the people's trace in Postgres (docs/adr/0034), so every replica of a stateless deployment appends to one place and none keeps a file of its own.
Package postgres keeps the people's trace in Postgres (docs/adr/0034), so every replica of a stateless deployment appends to one place and none keeps a file of its own.

Jump to

Keyboard shortcuts

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