Help us shape the future of Go! Take the 2026 Go Developer Survey

capability

package
v0.21.0 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: AGPL-3.0 Imports: 14 Imported by: 0

Documentation

Overview

Package capability serves the business-architecture area: the business capabilities an organisation must be able to perform, and the value streams whose stages they perform.

It is the registry Atlas owns. A capability record says what has to be done, who owns it in the business, what it has promised, and how it is currently done — by an executable process here, by a Worker, by a purchased system, or by a person. The last two are why the realization edge lives on this record rather than in a BPMN model: a capability nobody has automated has no model to carry the statement, and that is the normal state of most of a map on the day it is started.

Everything mutable about a realization is resolved when a record is read and stored nowhere. A record holding "deployed, version 7, healthy" would be a second, stale copy of the deployment registry within a week, which is the rule ADR-0189 §4 set for Panorama's bindings and the reason it set it.

The single-writer boundary is the runloop.Loop this service holds: every read and write of either store goes through it, and there is no other way from here to shared state (I3, ADR-0002/0147). Design-time only — nothing here reaches the event log, the processor, or recovery.

Index

Constants

View Source
const (
	// FindingUnrealized: a capability nothing is doing. The most useful row in the
	// report — the work still done by hand, or by a system nobody wrote down.
	FindingUnrealized = "capability.unrealized"
	// FindingRealizationMissing: a realization pointing at a process or Worker that is
	// not here. Either the map is ahead of the installation, or something was deleted
	// underneath it.
	FindingRealizationMissing = "realization.missing"
	// FindingProcessUnclaimed: a deployed process no capability claims. The other
	// direction, and the one that catches a map quietly going stale as delivery moves.
	FindingProcessUnclaimed = "process.unclaimed"
	// FindingRequiresUnknown: a dependency naming a capability that does not exist.
	FindingRequiresUnknown = "requires.unknown"
	// FindingStageEmpty: a value-stream stage no capability performs.
	FindingStageEmpty = "stage.empty"
	// FindingStageUnknown: a stage naming a capability that does not exist.
	FindingStageUnknown = "stage.unknown"
	// FindingCallUndeclared: one capability's process calls another capability's
	// process, and the caller never declared the dependency.
	FindingCallUndeclared = "call.undeclared"
	// FindingProcessShared: two capabilities claim the same deployed process. The map
	// exists to make ownership clear, and one implementation with two owners is the
	// opposite — so it is reported rather than resolved by whichever record happened to
	// be read last.
	FindingProcessShared = "process.shared"
	// FindingUnconfirmed: nobody has said this record still describes reality within
	// the installation's horizon — or nobody ever has.
	//
	// It is the one finding in this report that is *not* something Atlas verified.
	// Every other one states a fact it checked against the deployment registry or the
	// compiled model; this one states the absence of an assertion, which is the only
	// thing Atlas can honestly say about the owner, the scope and the SLAs. The detail
	// says which of the two it is, because "never confirmed" is a backlog to work
	// through once and "lapsed" is a review that slipped.
	FindingUnconfirmed = "capability.unconfirmed"
	// FindingStreamUnconfirmed is the same for a value stream.
	FindingStreamUnconfirmed = "value-stream.unconfirmed"
)

Finding kinds. The shape is <subject>.<problem>, so a UI can group them without a second table.

View Source
const (
	// ReasonProseThreshold: the SLA's threshold is free text with no machine-readable
	// counterpart on the record.
	ReasonProseThreshold = "its threshold is prose; add thresholdSeconds to have it measured"
	// ReasonKPIsAreDirections: a KPI is a direction with a goal, and Atlas has no way
	// to know which recorded number it refers to. The cycle time and outcome counts in
	// the same response are what a reader compares it against by hand.
	ReasonKPIsAreDirections = "" /* 137-byte string literal not displayed */
)

Reasons a declared figure is not measured.

View Source
const (
	CountedBasis = "counted from maintained per-element counters: all-time totals for this process definition, not restricted to the window"
	WalkedBasis  = "walked over the finished instances inside the window"
)

The two basis sentences, as constants so the response and the documentation cannot drift apart.

View Source
const (
	StateProposed   = "proposed"
	StateActive     = "active"
	StateDeprecated = "deprecated"
)

Lifecycle states of a capability. A field somebody sets, not a lifecycle Atlas drives — Atlas is the engine an organisation would model an approval workflow *in*.

View Source
const (
	KindAPI     = "api"
	KindEvent   = "event"
	KindMessage = "message"
	// KindManual is a first-class kind. A capability nobody has automated still has
	// an interface — an email with the customer's details, a form on a clerk's desk —
	// and writing it down is what makes the automation of it a described change rather
	// than a discovery.
	KindManual = "manual"
)

Interface kinds: how a capability is reached, or hands its result back.

View Source
const (
	ResourceTeam       = "team"
	ResourceSystem     = "system"
	ResourceCapability = "capability"
)

Resource kinds: what a capability draws on.

View Source
const (
	RealizationProcess = "process"
	RealizationWorker  = "worker"
	RealizationSystem  = "system"
	RealizationManual  = "manual"
)

Realization kinds: how a capability is currently done.

View Source
const (
	DirectionUp   = "up"
	DirectionDown = "down"
)

KPI directions: which way is better.

View Source
const (
	SLAInternal = "internal"
	SLAExternal = "external"
)

SLA scopes. Internal is a promise between two teams here; external is a contract or a regulator. They carry different consequences, so the record keeps them apart rather than leaving it to the wording.

View Source
const DefaultHorizonMonths = 12

DefaultHorizonMonths is how long a confirmation stays fresh when an installation has not said otherwise. Twelve, matching the two places this repository already dates something it cannot verify: a Worker Type's setup steps (ADR-0289) and a decision record's open question (ADR-0293). Inherited rather than invented.

View Source
const MeasurementNotice = "not measured: the KPIs and SLAs on a capability are declarations. " +
	"Atlas holds the data to compute them (per-element visit counters, the instance timeline) " +
	"but this build computes none of it."

MeasurementNotice is the standing answer to "and how is it doing?".

It is a field rather than documentation because the read returns goals and thresholds, and a client with a number-shaped field in front of it will render a number. Saying plainly that nothing here is measured is what stops a goal being displayed as an achievement. When the measurement slice lands, this changes and clients that read it stop being wrong.

Variables

This section is empty.

Functions

func FindingKinds

func FindingKinds() []string

FindingKinds is every kind, in report order. Served in the authoring subset so a client can render an empty state per kind rather than inferring the set from whatever a particular report happened to contain.

func NormalizeCapability

func NormalizeCapability(c *Capability)

NormalizeCapability trims every field a person typed and drops the blanks a form leaves behind, so that validation and storage see one shape. It defaults an unstated State to proposed: a capability somebody has written down but not yet agreed is exactly what "proposed" means, and it is the honest default for a record that arrives without one.

func NormalizeValueStream

func NormalizeValueStream(v *ValueStream)

NormalizeValueStream is NormalizeCapability's twin for a stream and its stages.

func ValidateCapability

func ValidateCapability(c Capability) []string

ValidateCapability reports everything wrong with a record, rather than the first thing: an author fixing one field at a time through a form would otherwise make one round trip per mistake.

What it deliberately does *not* refuse is a capability with no realization, no KPI, no SLA and no interface. That is a capability somebody has named and not yet worked on, it is the majority of any new map, and it is the row the gap report exists to count. A validator that demanded completeness would make the map impossible to start and would be refusing the method's own first step.

func ValidateValueStream

func ValidateValueStream(v ValueStream) []string

ValidateValueStream reports everything wrong with a stream.

It accepts the same capability key in several stages, and that is deliberate: an end-to-end capability spans several stages of a stream, which is the method's own accepted inconsistency. A validator "fixing" it would be enforcing a containment tree the method rejects.

Types

type AuthoringSubset

type AuthoringSubset struct {
	States           []string `json:"states"`
	InterfaceKinds   []string `json:"interfaceKinds"`
	ResourceKinds    []string `json:"resourceKinds"`
	RealizationKinds []string `json:"realizationKinds"`
	Directions       []string `json:"kpiDirections"`
	SLAScopes        []string `json:"slaScopes"`
	FindingKinds     []string `json:"gapFindingKinds"`
	// KeyPattern is the regular expression a key must match, so a form can refuse a
	// bad key while it is being typed with the same rule the server writes through.
	KeyPattern string `json:"keyPattern"`
	// DefaultHorizonMonths is how long a confirmation stays fresh where an installation
	// has not said otherwise. The horizon actually in force is an installation setting
	// and travels with the answer that applied it, never with this one — the subset
	// says what this build does, not how this server is configured.
	DefaultHorizonMonths int `json:"defaultConfirmationHorizonMonths"`
	// Hierarchy states, in the surface itself, that there is none. A client author
	// looking for a parent field finds the reason instead of assuming an oversight.
	Hierarchy string `json:"hierarchy"`
}

AuthoringSubset is every vocabulary this build accepts, served rather than duplicated in the browser. A picker offering a word the write path refuses is a promise the server breaks, which is why the list has one definition.

func Subset

func Subset() AuthoringSubset

Subset returns the authoring subset.

type Call

type Call struct {
	CallerProcessID string `json:"callerProcessId"`
	ElementID       string `json:"elementId"`
	CalledProcessID string `json:"calledProcessId"`
	// Resolved says the call would start a child on this server rather than park.
	// An unresolved call is somebody else's finding (the call-activity inventory
	// already reports it), so this area only reads the resolved ones.
	Resolved bool `json:"resolved"`
}

Call is one call activity between deployed processes, with the target it resolves to on this server.

It is the input to the one finding worth the most: a call crossing from one capability's process into another's that the caller never declared. It is compared against Requires and never merged into it — a declared dependency with no call is the normal case, since the method's black box is usually a REST call rather than a call activity.

type Capability

type Capability struct {
	// Key is the identity, everywhere: the URL, the filename, what Requires and a
	// value stream's stages name, and what an export carries. There is deliberately no
	// second local id — a random one would make an exported document unreadable and
	// force an import into another installation to remap it, and the map is meant to
	// travel. The cost is that the key is not renameable in place; see the record.
	Key string `json:"key"`
	// Name is what a person calls it; Summary is the one line a list shows.
	Name    string `json:"name"`
	Summary string `json:"summary,omitempty"`
	// Scope says what this capability is responsible for and — the half that earns
	// the field — what it is not. It is where boundary arguments are settled.
	Scope string `json:"scope,omitempty"`
	// Inputs and Outputs are the interface: what triggers the capability and with
	// what data, and what it hands back.
	Inputs  []Interface `json:"inputs,omitempty"`
	Outputs []Interface `json:"outputs,omitempty"`
	// Owner is the *business* owner, accountable for how the capability performs.
	// Not the application's sharing scope, which decides who may open and change a
	// model; those are regularly different people.
	Owner Owner `json:"owner"`
	// Resources are the teams, systems and other capabilities this one draws on.
	Resources []Resource `json:"resources,omitempty"`
	// Realizations are how it is currently done. Empty is a meaningful state and the
	// single most useful line in the record: it says the work is not automated here.
	Realizations []Realization `json:"realizations,omitempty"`
	// Requires are the capability keys this one depends on, as black boxes.
	Requires []string `json:"requires,omitempty"`
	// KPIs and SLAs are declarations. Nothing in this build computes one, and no read
	// returns a live figure — measurement is a later slice, and saying so here is what
	// stops the field from being read as a number.
	KPIs []KPI `json:"kpis,omitempty"`
	SLAs []SLA `json:"slas,omitempty"`
	// Tags are the only classification there is. A view of the map — by business area,
	// by "implements an end-to-end process", by an imported reference model's level —
	// is a tag query, never a tree walk.
	Tags []string `json:"tags,omitempty"`
	// State is where the capability is in its own life: proposed, active, deprecated.
	// A field somebody sets, not a lifecycle Atlas drives.
	State string `json:"state"`
	// Confirmation is when a person last said this record still describes reality, and
	// who said it. It is set by an explicit confirmation and by no edit — see
	// [Confirmation] and ADR-0304.
	Confirmation Confirmation `json:"confirmation"`
	// Revision is optimistic concurrency: a write against a stale one is refused
	// rather than silently overwriting somebody else's edit.
	Revision  int64  `json:"revision"`
	CreatedAt int64  `json:"createdAt"`
	CreatedBy string `json:"createdBy,omitempty"`
	UpdatedAt int64  `json:"updatedAt"`
	UpdatedBy string `json:"updatedBy,omitempty"`
}

Capability is one business capability: what the organisation must be able to do, stated independently of how it is currently done.

The shape is the method's own definition — scope, input, output, owner, resources, metrics and controls — and two of its rules are structural rather than advisory:

  • There is no parent, and no children. The method's advice is to resist a capability hierarchy, because an "end-to-end" capability is regularly invoked from inside another one, so any tree is wrong from some direction, and the argument about which tree is right consumes the time the architecture was meant to save. An end-to-end process is a capability carrying a Tag. Refusing the field is what keeps that a property of the system; TestCapabilityHasNoParent holds it.
  • Requires names other capabilities as black boxes. It is declared, never derived from the model: from the caller's side the required capability is normally a service task or a message, not a call activity, so a derived graph would systematically miss it. The gap report compares the two and never merges them.

type CapabilitySummary

type CapabilitySummary struct {
	Key     string   `json:"key"`
	Name    string   `json:"name"`
	Summary string   `json:"summary,omitempty"`
	Owner   Owner    `json:"owner"`
	State   string   `json:"state"`
	Tags    []string `json:"tags,omitempty"`
	// RealizationCount and Realized answer the one question a list of capabilities is
	// opened to ask: which of these is anybody actually doing anything about.
	RealizationCount int  `json:"realizationCount"`
	Realized         bool `json:"realized"`
	RequiresCount    int  `json:"requiresCount"`
	KPICount         int  `json:"kpiCount"`
	SLACount         int  `json:"slaCount"`
	// ConfirmedAt and Stale are the review half of the row, the twin of Realized: one
	// list answers "what does nobody automate", the other "what has nobody re-read".
	ConfirmedAt int64  `json:"confirmedAt,omitempty"`
	Stale       bool   `json:"stale"`
	Revision    int64  `json:"revision"`
	UpdatedAt   int64  `json:"updatedAt"`
	UpdatedBy   string `json:"updatedBy,omitempty"`
}

CapabilitySummary is the list representation: enough to render a row and decide what to open, without the whole definition.

type CaseStats

type CaseStats struct {
	// Cases is how many finished instances the window held.
	Cases int64 `json:"cases"`
	// MeanSeconds is the arithmetic mean cycle time, 0 when Cases is 0.
	MeanSeconds float64 `json:"meanSeconds"`
	MinSeconds  int64   `json:"minSeconds"`
	MaxSeconds  int64   `json:"maxSeconds"`
}

CaseStats is what a walk over finished instances in the window found. Every field is streaming — a running count, sum, minimum and maximum — so the walk holds one instance at a time however many it reads.

There are no percentiles, and their absence is a decision rather than an omission. A percentile needs every value at once, so the memory it costs grows with the window, which is the one thing a windowed reading is designed not to do. What a percentile is usually wanted *for* here is an SLA — "90% within ten minutes" — and that is answerable by counting, which SLAAttainment does.

type Checked

type Checked struct {
	Capabilities int `json:"capabilities"`
	ValueStreams int `json:"valueStreams"`
	Processes    int `json:"processes"`
	Calls        int `json:"calls"`
}

Checked is the size of what the report looked at.

type Clock

type Clock func() time.Time

Clock supplies timestamps, injected so tests are deterministic.

type Confirmation

type Confirmation struct {
	// At is unix seconds, zero when the record has never been confirmed. Zero is not
	// "confirmed at the epoch": it is the honest statement that nobody has said this is
	// true since the field existed, and the report distinguishes the two in words.
	At int64 `json:"at,omitempty"`
	// By is the principal who confirmed. Empty on an unconfirmed record, and empty on a
	// confirmation made with auth off, where there is no principal to name.
	By string `json:"by,omitempty"`
	// With names who was *asked*, where that is somebody other than the confirmer.
	// Empty means the confirmer spoke for the record alone — a legitimate confirmation
	// and a weaker one, which this says rather than implying the opposite.
	//
	// It is another claim Atlas cannot verify, and it earns its place for the reason
	// Owner does: the alternative is that who was asked lives only in the head of
	// whoever asked them.
	With string `json:"with,omitempty"`
	// Note is one line saying what the review found, replaced on each confirmation
	// rather than appended. This area is a correlation surface, not a log (ADR-0189).
	Note string `json:"note,omitempty"`
}

Confirmation is the freshness half of a record: when a person last said it still describes reality, who said it, and who they asked.

It exists because the gap report can check a realization against the deployment registry and can check nothing else. The owner, the scope and the SLAs — the fields anybody actually acts on — are prose about people and promises, and a map that is confidently wrong about those is worse than no map. Atlas cannot verify them; it can record when somebody last stood behind them, which is the honest thing it can do.

The shape is the third instance of one pattern in this tree: a Worker Type's setup steps carry a checked date (ADR-0289) and a decision record's open question carries one (ADR-0293). Both of those are enforced by a Go test over content in this repository. This one cannot be — it is runtime data in somebody else's installation — so the gap report is the enforcement instead.

func (Confirmation) Confirmed

func (c Confirmation) Confirmed() bool

Confirmed reports whether anybody has ever confirmed the record.

func (Confirmation) StaleAt

func (c Confirmation) StaleAt(now time.Time, horizonMonths int) bool

StaleAt reports whether the confirmation has lapsed as of now, given the installation's horizon in months. A record nobody has ever confirmed is stale: the whole point is that nobody has asserted it.

type ConfirmationView

type ConfirmationView struct {
	Confirmation
	// HorizonMonths is the interval this answer applied. Published rather than assumed,
	// because it is an installation setting: a reader seeing "not stale" is entitled to
	// know whether that means "confirmed last month" or "the horizon is a century".
	HorizonMonths int `json:"horizonMonths"`
	// Stale is whether the confirmation has lapsed, or was never made.
	Stale bool `json:"stale"`
	// Ever is whether anybody has confirmed this at all. Stale without Ever is a record
	// nobody has ever stood behind, which is different work from one that lapsed.
	Ever bool `json:"ever"`
	// SelfConfirmed is true when the confirmer is also the last person to have edited
	// the record — the map confirming itself.
	//
	// It is shown and never reported. In a four-person installation the architect is
	// the only person who *can* confirm, so a finding here would fire on the normal
	// case, and a report that fires on the normal case stops being read. Shown beside
	// the confirmer and who they asked, it is one of three facts a reader weighs.
	SelfConfirmed bool `json:"selfConfirmed"`
}

ConfirmationView is a confirmation as a read renders it: what the record says, plus the two judgements a reader would otherwise have to make themselves.

type CoverageReport

type CoverageReport struct {
	Capability Capability `json:"capability"`
	// Realized is whether anything at all currently does this. It is the summary line
	// of the whole read, and it counts a clerk and a purchased system as realizations,
	// because they are.
	Realized     bool                  `json:"realized"`
	Realizations []ResolvedRealization `json:"realizations"`
	// Requires is what this capability depends on, each with the promise it has made.
	// The dependency plus the SLA is the whole interface, which is what lets an
	// end-to-end target be reasoned about without opening any box beneath it.
	Requires []Dependency `json:"requires"`
	// RequiredBy is the reverse: who would notice if this stopped.
	RequiredBy []Dependent `json:"requiredBy"`
	// ValueStreams are the streams whose stages name this capability, with the stages
	// themselves — an end-to-end capability is named by every stage it spans.
	ValueStreams []StreamUse `json:"valueStreams"`
	// Confirmation is the freshness half: when a person last said this record's prose
	// still describes reality, who said it, who they asked, and whether that has
	// lapsed. It sits beside the SLAs it is about — a reader looking at what a
	// capability promised should see, in the same answer, when anybody last stood
	// behind that.
	Confirmation ConfirmationView `json:"confirmation"`
	// Measurement is [MeasurementNotice]. See its comment for why it is on the wire.
	Measurement string `json:"measurement"`
}

CoverageReport is one capability, resolved.

func Coverage

func Coverage(c Capability, all []Capability, streams []ValueStream, land Landscape,
	now time.Time, horizonMonths int) CoverageReport

Coverage resolves one capability against the map and the landscape.

Pure, like Gaps, and for the same reason: it can then be tested against a landscape written by hand, and it never needs the run loop.

type DeletionResult

type DeletionResult struct {
	Deleted string `json:"deleted"`
	// RequiredBy and Stages are the references now pointing at nothing.
	RequiredBy []string   `json:"requiredBy,omitempty"`
	Stages     []StageRef `json:"stages,omitempty"`
	// ValueStreams names the streams those stages belong to, in the same order.
	ValueStreams []string `json:"valueStreams,omitempty"`
}

DeletionResult is what a delete says about what it left behind.

type Dependency

type Dependency struct {
	Key string `json:"key"`
	// Known is whether the map has this capability at all. False is a finding the gap
	// report also raises; it is here because the caller of this read is looking at the
	// dependency list and should not have to fetch a second document to learn that one
	// of them names nothing.
	Known    bool   `json:"known"`
	Name     string `json:"name,omitempty"`
	State    string `json:"state,omitempty"`
	Realized bool   `json:"realized"`
	Owner    Owner  `json:"owner,omitempty"`
	SLAs     []SLA  `json:"slas,omitempty"`
}

Dependency is one required capability as its caller sees it: a black box with a name, a state, and whatever it has promised.

type Dependent

type Dependent struct {
	Key   string `json:"key"`
	Name  string `json:"name,omitempty"`
	State string `json:"state,omitempty"`
	Owner Owner  `json:"owner,omitempty"`
}

Dependent is one capability that requires this one.

type ElementCount

type ElementCount struct {
	ElementID string `json:"elementId"`
	Name      string `json:"name,omitempty"`
	Count     int64  `json:"count"`
}

ElementCount is one element of a process and a number the engine counted for it.

Name is the element's own label, and today it is always empty: the compiler interns an element name only for a user task, so an end event's name is read from the model and dropped. The field is here rather than omitted because the method asks authors to name their end events distinctly and a reader is meant to read those names — so the shape a client codes against should be the one that will carry them. Until it does, ElementID is the join key and the process XML the API already serves is where the label is.

type Finding

type Finding struct {
	Kind           string `json:"kind"`
	CapabilityKey  string `json:"capabilityKey,omitempty"`
	RequiredKey    string `json:"requiredKey,omitempty"`
	ValueStreamKey string `json:"valueStreamKey,omitempty"`
	StageKey       string `json:"stageKey,omitempty"`
	ApplicationKey string `json:"applicationKey,omitempty"`
	ProcessID      string `json:"processId,omitempty"`
	ElementID      string `json:"elementId,omitempty"`
	// WorkerRef is the Worker a missing worker realization names. It is a coordinate
	// like the process fields beside it, so a reader in another language can say which
	// Worker is missing without parsing the English detail (ADR-0267).
	WorkerRef string `json:"workerRef,omitempty"`
	// Detail says what to do about it in one sentence. A finding that only names a
	// coordinate makes the reader guess.
	Detail string `json:"detail"`
}

Finding is one disagreement between the map and the installation. The fields are the coordinates of the thing it is about; a finding fills the ones that apply and leaves the rest empty.

There is no severity. A severity scale would be this package deciding how much somebody else's architecture gap matters, which it cannot know: an unrealized capability is a triumph of honesty in one organisation and an emergency in another. The kind says what it is; what it is worth is the reader's.

type GapReport

type GapReport struct {
	// Counts carries every kind, including the zeroes. A report that listed only the
	// kinds it happened to find would leave a UI unable to say "nothing wrong here".
	Counts   map[string]int `json:"counts"`
	Findings []Finding      `json:"findings"`
	// HorizonMonths is the confirmation horizon this report applied. Published rather
	// than assumed: it is an installation setting, so a reader seeing no unconfirmed
	// findings is entitled to know whether the map is fresh or the horizon is a century.
	HorizonMonths int `json:"horizonMonths"`
	// Restricted is how many things in the landscape the caller's access hid from this
	// comparison — counted once each, however many ways the comparison met them.
	// Published rather than swallowed: a report that quietly checks less than it
	// claims is worse than one that admits its blind spot.
	Restricted int `json:"restricted"`
	// Checked says what the comparison ran over, so a suspiciously empty report can be
	// told apart from an empty installation.
	Checked Checked `json:"checked"`
}

GapReport is the whole comparison.

func Gaps

func Gaps(caps []Capability, streams []ValueStream, land Landscape, now time.Time, horizonMonths int) GapReport

Gaps compares a capability map against the landscape.

Pure: it reads its three arguments and touches nothing else, which is what lets it be tested against a landscape written by hand and what keeps it off the run loop.

type HorizonResolver

type HorizonResolver func() (int, error)

HorizonResolver reads how many months a confirmation stays fresh for, from the installation's settings. Called from inside a loop turn, like the landscape resolver.

It is a function rather than a field because an operator can change it while the server runs, and a read that answered from a value captured at construction would keep applying yesterday's policy.

type Interface

type Interface struct {
	Name string `json:"name"`
	// Kind is how it is reached: an API call, an event, a message, or a human being
	// handed something. Manual is a first-class kind because a capability nobody has
	// automated still has an interface, and it is the one worth writing down.
	Kind        string `json:"kind"`
	Description string `json:"description,omitempty"`
}

Interface is one side of a capability's contract: a trigger it accepts or a result it hands back.

type KPI

type KPI struct {
	Name   string `json:"name"`
	Metric string `json:"metric"`
	Goal   string `json:"goal,omitempty"`
	// Direction says which way is better, so a reader knows whether a number moving
	// up is good news. Empty where neither is.
	Direction string `json:"direction,omitempty"`
	Note      string `json:"note,omitempty"`
}

KPI is a direction with a goal. Nobody is fined for missing one.

type Landscape

type Landscape struct {
	Processes []Process
	Workers   []Worker
	Calls     []Call
}

Landscape is the whole of what this area is told about the installation. Built on the run loop, filtered for the requesting principal, and read off it.

type LandscapeResolver

type LandscapeResolver func(r *http.Request) (Landscape, error)

LandscapeResolver builds the picture of the installation this area compares the map against, filtered for the requesting principal. It is called from inside a loop turn — it reads the deployment registry, the application store and the worker registry, which only the loop may touch — and must not dispatch onto the loop again.

type Measurement

type Measurement struct {
	Key  string `json:"key"`
	Name string `json:"name"`
	// Window is what the walked figures cover. The counted ones do not.
	Window     Window `json:"window"`
	WindowDays int    `json:"windowDays"`
	// CountedBasis and WalkedBasis say, in the response itself, which figures rest on
	// what. A client rendering this does not have to have read the ADR to label its
	// own axes honestly.
	CountedBasis string `json:"countedBasis"`
	WalkedBasis  string `json:"walkedBasis"`

	Processes []ProcessMeasurement `json:"processes"`
	// SLAs are the declared commitments that could be measured; NotMeasured names
	// every declared figure that could not, with the reason.
	SLAs        []SLAAttainment `json:"slas"`
	NotMeasured []NotMeasured   `json:"notMeasured"`
	// Unrealizable is true when the capability names no realisation this server runs,
	// so there is nothing to measure and that is a fact about the map rather than a
	// failure of the reading.
	Unrealizable bool `json:"unrealizable,omitempty"`
}

Measurement is the whole answer for one capability.

func Measure

func Measure(c Capability, w Window, recorded []RecordedProcess) Measurement

Measure folds what was recorded into the answer for one capability.

now is not a parameter: everything time-dependent was decided when the window was parsed, and taking a clock here would let the answer drift between the read and the arithmetic over it.

type MeasurementResolver

type MeasurementResolver func(r *http.Request, realizations []string, w Window) ([]RecordedProcess, error)

MeasurementResolver reads what the engine recorded for a capability's realising processes, over one window, filtered for the caller.

It is injected rather than implemented here for the same reason the landscape resolver is: this package holds a design-time register and must not reach into the engine. But the reason it is a *separate* resolver from the landscape one is sharper than symmetry — this read walks instances, so it must run **off the run loop** (ADR-0239), and the coverage and gap reads run on it. Handing both to one resolver would make the difference invisible at the call site, and the difference is the one that decides whether a query can stop the engine.

type NotMeasured

type NotMeasured struct {
	Kind   string `json:"kind"` // "kpi" or "sla"
	Name   string `json:"name"`
	Reason string `json:"reason"`
}

NotMeasured is one declared figure Atlas did not compute, and why.

A declaration nothing measured is the normal case and stays the normal case: the point of the record is that somebody can write down what a capability is held to before anything can check it. What this type prevents is the reader having to infer from an absence whether the number is missing, zero, or uncomputable.

type Owner

type Owner struct {
	Name    string `json:"name,omitempty"`
	Role    string `json:"role,omitempty"`
	Contact string `json:"contact,omitempty"`
	// Username links the owner to an Atlas account where there is one. Advisory: it
	// grants nothing and is not resolved against the user store, because an owner who
	// leaves must not make the record unreadable.
	Username string `json:"username,omitempty"`
}

Owner is who is accountable, in business terms. Free text with an optional Atlas account, and in that order deliberately: the person accountable for a capability is frequently an SVP or a department head who has never signed in here, and demanding a principal would either exclude them or fill the directory with ghosts.

type Process

type Process struct {
	// ApplicationKey is the portable application key (ADR-0134), empty for a process
	// deployed outside any application. A realization addresses this, never the local
	// application id, so the reference survives the map being moved.
	ApplicationKey  string `json:"applicationKey,omitempty"`
	ApplicationName string `json:"applicationName,omitempty"`
	ProcessID       string `json:"processId"`
	Name            string `json:"name,omitempty"`
	Version         int32  `json:"version"`
	// Inactive marks a deployment an operator has switched off (ADR-0119). It still
	// exists, so it is not a missing realization — a different finding entirely.
	Inactive bool `json:"inactive"`
	// ActiveInstances is the O(1) maintained per-definition counter (ADR-0080), never
	// a scan: this read must not grow with the instance population.
	ActiveInstances int `json:"activeInstances"`
	// CanView is whether the requesting principal may see this process at all. A
	// realization pointing at one they may not see is reported as restricted rather
	// than as missing — the difference between "you cannot see this" and "this is not
	// there" is the whole value of the answer.
	CanView bool `json:"-"`
}

Process is one deployed process as the registry sees it.

type ProcessMeasurement

type ProcessMeasurement struct {
	ProcessID string `json:"processId"`
	Name      string `json:"name,omitempty"`
	// Restricted marks a realisation this caller may not see. Its figures are absent
	// rather than zero, for the reason the gap report has the same distinction: zero
	// is a finding and "you may not look" is not.
	Restricted bool `json:"restricted,omitempty"`
	// Deployed is false for a realisation this server does not currently run, which is
	// a fact about the installation rather than about the capability.
	Deployed bool `json:"deployed"`

	// Outcomes and Cancellations are counted, therefore all-time.
	Outcomes      []ElementCount `json:"outcomes"`
	Cancellations []ElementCount `json:"cancellations,omitempty"`
	// Cases is walked, therefore windowed.
	Cases CaseStats `json:"cases"`
}

ProcessMeasurement is what one realising process recorded. A capability realised by several processes gets one of these each rather than a sum: the processes are different implementations of the same capability, and adding their cycle times together would produce a number describing nothing.

type Realization

type Realization struct {
	Kind string `json:"kind"`
	// ApplicationKey and ProcessID address a process realization. The application key
	// is the portable one (ADR-0134), not a local id, so the reference survives the
	// map being moved to another installation.
	ApplicationKey string `json:"applicationKey,omitempty"`
	ProcessID      string `json:"processId,omitempty"`
	// WorkerRef addresses a Worker realization: one configured target and identity of
	// a Worker Type (ADR-0203).
	WorkerRef string `json:"workerRef,omitempty"`
	// Note is what a system or manual realization is, in words. Required for those
	// two kinds, because "a system does it" with no name is not a realization.
	Note string `json:"note,omitempty"`
}

Realization is how a capability is currently done. Four kinds, and the two that are not Atlas resources are the reason this edge lives on the record rather than in a BPMN model: a capability performed by a purchased system or by a person is the normal state before the work starts, which is exactly when the map is most useful.

type RecordedProcess

type RecordedProcess struct {
	ProcessID string
	Name      string
	// Restricted: the caller may not see this process. Its numbers are absent, and
	// absent is not zero.
	Restricted bool
	// Deployed: this server currently runs it. A realisation naming a process that is
	// not deployed here is a fact about the installation, and the gap report is where
	// it is a finding; here it only means there is nothing to measure.
	Deployed bool

	// EndEvents and Cancellations are all-time counters (ADR-0080).
	EndEvents     []ElementCount
	Cancellations []ElementCount
	// Durations are the cycle times, in seconds, of the finished instances inside the
	// window — one entry per case.
	//
	// A slice rather than a pre-folded summary because the SLA attainment needs to
	// count against a threshold and the cycle-time summary needs a mean, and folding
	// twice over one slice in memory is cheaper than walking the store twice. The
	// resolver bounds its length by bounding the window, which is the same bound that
	// makes the walk affordable at all.
	Durations []int64
}

RecordedProcess is what a resolver read for one realising process. It is the shape the engine's three data sources reduce to, and nothing in this package knows how they were read.

type ResolvedRealization

type ResolvedRealization struct {
	Realization
	// Resolved says the thing this realization names is here. A system or manual
	// realization is always resolved: Atlas cannot check either, and reporting them
	// as unresolved would read as broken when the truth is only that the work happens
	// somewhere Atlas cannot see.
	Resolved bool `json:"resolved"`
	// Restricted says the caller may not see what this points at. It is not Resolved
	// and it is not missing — asserting either would tell the caller something they
	// have no right to know, or something untrue.
	Restricted bool `json:"restricted"`
	// Name, ApplicationName, Version, Inactive, ActiveInstances are the live facts,
	// resolved now and stored nowhere. All empty on a restricted entry.
	Name            string `json:"name,omitempty"`
	ApplicationName string `json:"applicationName,omitempty"`
	WorkerType      string `json:"workerType,omitempty"`
	Version         int32  `json:"version,omitempty"`
	Inactive        bool   `json:"inactive,omitempty"`
	ActiveInstances int    `json:"activeInstances,omitempty"`
}

ResolvedRealization is one realization with what the installation says about it.

type Resource

type Resource struct {
	Kind string `json:"kind"`
	Name string `json:"name"`
	// Ref points at the thing where Atlas has a name for it — a capability key for
	// kind "capability", a Worker reference or a system identifier otherwise. Free
	// text; a resource is a description of reality, not a foreign key.
	Ref string `json:"ref,omitempty"`
}

Resource is something a capability draws on: a team, a system, or another capability.

type SLA

type SLA struct {
	Name      string `json:"name"`
	Metric    string `json:"metric"`
	Threshold string `json:"threshold"`
	// ThresholdSeconds is the same threshold as a number, where the author chose to
	// give one. It is optional and deliberately separate from Threshold rather than
	// replacing it: "within five business days" is what the business agreed and what
	// belongs in the record, and no parser should be asked to decide what a business
	// day means here.
	//
	// Supplying it is what makes the SLA measurable — a measurement compares against
	// this and reports nothing when it is absent, rather than guessing. So the record
	// stays writable in prose by somebody who has no number, and rewards the one who
	// does.
	ThresholdSeconds int64  `json:"thresholdSeconds,omitempty"`
	Window           string `json:"window,omitempty"`
	// Scope is internal (a promise between two teams here) or external (a contract or
	// a regulator). The two carry different consequences, so the record keeps them
	// apart rather than leaving it to the wording.
	Scope        string `json:"scope"`
	Counterparty string `json:"counterparty,omitempty"`
	Note         string `json:"note,omitempty"`
}

SLA is a commitment: a threshold, a window and somebody it is owed to. The difference from a KPI is the element of commitment, and it is why an end-to-end target can be distributed across the capabilities beneath it without opening any of them.

type SLAAttainment

type SLAAttainment struct {
	Name   string `json:"name"`
	Metric string `json:"metric"`
	// ThresholdSeconds is what was compared against, echoed so the figure can be
	// checked without fetching the record.
	ThresholdSeconds int64 `json:"thresholdSeconds"`
	// Within and Cases are the counts: Within is how many finished cases in the
	// window met the threshold, Cases how many there were.
	Within int64 `json:"within"`
	Cases  int64 `json:"cases"`
	// Share is Within/Cases, 0 when there were no cases. A rate over no cases is not
	// 100% and not 0%; the reader is meant to look at Cases, which is why it is here.
	Share float64 `json:"share"`
}

SLAAttainment is a declared SLA measured against what ran.

It is reported only for an SLA that carries a machine-readable threshold. A threshold written as prose — "within five business days" — is not something Atlas can turn into a number without guessing what a business day is here, and a guessed SLA is worse than an unmeasured one: it would be a number somebody acts on that nobody authored.

type Service

type Service struct {

	// Limits are the installation's resource budgets. New sets them to
	// [limits.Default]; the server overwrites them with its own once it has read the
	// environment (ADR-0291).
	Limits limits.Limits
	// contains filtered or unexported fields
}

Service serves the business-architecture area. Build it with New.

func New

func New(loop *runloop.Loop, caps *Store, streams *StreamStore, landscape LandscapeResolver,
	horizon HorizonResolver, now Clock) *Service

New builds the service over its two store directories.

func (*Service) HandleConfirmCapability

func (s *Service) HandleConfirmCapability(w http.ResponseWriter, r *http.Request)

HandleConfirmCapability records that somebody has read a capability and says it still describes reality.

func (*Service) HandleConfirmValueStream

func (s *Service) HandleConfirmValueStream(w http.ResponseWriter, r *http.Request)

HandleConfirmValueStream is the value stream's twin: a stream's owner and KPIs decay exactly as a capability's do.

func (*Service) HandleCoverage

func (s *Service) HandleCoverage(w http.ResponseWriter, r *http.Request)

HandleCoverage resolves one capability against the installation: what actually does it, what it depends on and what those have promised, who depends on it, and which value-stream stages it performs.

func (*Service) HandleCreateCapability

func (s *Service) HandleCreateCapability(w http.ResponseWriter, r *http.Request)

HandleCreateCapability files a new capability.

func (*Service) HandleCreateValueStream

func (s *Service) HandleCreateValueStream(w http.ResponseWriter, r *http.Request)

HandleCreateValueStream files a new value stream.

func (*Service) HandleDeleteCapability

func (s *Service) HandleDeleteCapability(w http.ResponseWriter, r *http.Request)

HandleDeleteCapability removes a capability and says what now dangles.

It does not refuse a capability others reference, and it does not cascade. Refusing would mean editing every referrer before retiring anything, which makes a map that cannot be evolved; cascading would silently rewrite records the caller never mentioned. So it deletes the one thing asked for and answers with the references it has just broken — which the gap report will also carry, but a caller learning it a day later has already moved on.

This is why the route answers 200 with a body rather than 204: there is something to say.

func (*Service) HandleDeleteValueStream

func (s *Service) HandleDeleteValueStream(w http.ResponseWriter, r *http.Request)

HandleDeleteValueStream removes a value stream. Nothing references a stream, so unlike a capability's delete there is nothing to report and this answers 204.

func (*Service) HandleGaps

func (s *Service) HandleGaps(w http.ResponseWriter, r *http.Request)

HandleGaps compares the whole map against the installation.

Everything it reads is design-time size — the map, the deployment registry, the call-activity graph — plus one O(1) maintained counter per definition (ADR-0080). Nothing here grows with the instance population, which is why it may run on the run loop at all (ADR-0239).

func (*Service) HandleGetCapability

func (s *Service) HandleGetCapability(w http.ResponseWriter, r *http.Request)

HandleGetCapability returns one whole capability.

func (*Service) HandleGetValueStream

func (s *Service) HandleGetValueStream(w http.ResponseWriter, r *http.Request)

HandleGetValueStream returns one whole value stream.

func (*Service) HandleListCapabilities

func (s *Service) HandleListCapabilities(w http.ResponseWriter, r *http.Request)

HandleListCapabilities lists the map, filtered.

The filters are the ones the method actually asks of a flat list: by tag (the only classification there is), by state, by whether anything realizes it, and by a substring of the name or key. `realized=false` is the adoption backlog, which is the query this list mostly exists to answer.

func (*Service) HandleListValueStreams

func (s *Service) HandleListValueStreams(w http.ResponseWriter, r *http.Request)

HandleListValueStreams lists the value streams, optionally by tag.

func (*Service) HandleMeasurement

func (s *Service) HandleMeasurement(w http.ResponseWriter, r *http.Request)

HandleMeasurement answers what a capability's declared KPIs and SLAs actually came to, over a window the caller must name.

Unlike every other read in this service it does not go through [Service.dispatch]. The resolver takes what it needs from the loop itself — a snapshot handle and the deployment metadata — and does the walking outside it, because the walk is linear in the instances the window holds and holding Atlas's single writer for that is how one operator's question becomes everybody's outage.

func (*Service) HandleSubset

func (s *Service) HandleSubset(w http.ResponseWriter, _ *http.Request)

HandleSubset serves every vocabulary this build accepts, plus the statement that there is no hierarchy. Served rather than duplicated in the browser: a picker offering a word the write path refuses is a promise the server breaks.

func (*Service) HandleUpdateCapability

func (s *Service) HandleUpdateCapability(w http.ResponseWriter, r *http.Request)

HandleUpdateCapability replaces a capability's content.

The whole record is sent rather than a patch, for the reason the information model takes a whole document: a patch language would be a second way to say everything the record already says. A stale revision is refused as a conflict.

The key cannot change. It is the identity in the URL, on disk, in every `requires` and in every value-stream stage, so renaming it in place would silently break every reference to it. Renaming is a deliberate gap, not an oversight: export, edit, import.

func (*Service) HandleUpdateValueStream

func (s *Service) HandleUpdateValueStream(w http.ResponseWriter, r *http.Request)

HandleUpdateValueStream replaces a value stream's content. The key cannot change, for the reason a capability's cannot.

func (*Service) SetMeasurementResolver

func (s *Service) SetMeasurementResolver(m MeasurementResolver)

SetMeasurementResolver wires the off-loop runtime read (ADR-0239). Called once at start-up, before the service serves anything.

type Stage

type Stage struct {
	Key          string   `json:"key"`
	Name         string   `json:"name"`
	Description  string   `json:"description,omitempty"`
	Capabilities []string `json:"capabilities,omitempty"`
}

Stage is one step of a value stream and the capabilities that perform it.

type StageRef

type StageRef struct {
	Key  string `json:"key"`
	Name string `json:"name,omitempty"`
}

StageRef names one stage of a value stream.

type Store

type Store struct {
	*sidecar.Store[Capability]
}

Store holds capabilities.

func NewStore

func NewStore(dir string) (*Store, error)

NewStore opens the capability directory. Records list by name, then by key, so a list reads the way a person would sort it and two capabilities sharing a name still come back in a stable order.

type StreamStore

type StreamStore struct {
	*sidecar.Store[ValueStream]
}

StreamStore holds value streams.

func NewStreamStore

func NewStreamStore(dir string) (*StreamStore, error)

NewStreamStore opens the value-stream directory, ordered like the capability store.

type StreamUse

type StreamUse struct {
	Key    string     `json:"key"`
	Name   string     `json:"name,omitempty"`
	Stages []StageRef `json:"stages"`
}

StreamUse is one value stream that uses this capability, and where.

type ValueStream

type ValueStream struct {
	Key         string   `json:"key"`
	Name        string   `json:"name"`
	Description string   `json:"description,omitempty"`
	Owner       Owner    `json:"owner"`
	Stages      []Stage  `json:"stages,omitempty"`
	KPIs        []KPI    `json:"kpis,omitempty"`
	Tags        []string `json:"tags,omitempty"`
	// Confirmation is the same freshness record a capability carries, for the same
	// reason: a stream's owner and KPIs decay exactly as a capability's do.
	Confirmation Confirmation `json:"confirmation"`
	Revision     int64        `json:"revision"`
	CreatedAt    int64        `json:"createdAt"`
	CreatedBy    string       `json:"createdBy,omitempty"`
	UpdatedAt    int64        `json:"updatedAt"`
	UpdatedBy    string       `json:"updatedBy,omitempty"`
}

ValueStream is the high-level, ordered activity the organisation performs to meet a customer need. Its stages name the capabilities that perform them.

Ordered, because the whole purpose of a value stream is to show the sequence; a capability list is unordered because its whole purpose is to be composable.

The method's own inconsistency is accepted rather than engineered away: an end-to-end process spans several stages *and* is itself a capability, so such a capability is named by every stage it spans, and a stream cannot be zoomed into through a strict containment tree. Inventing a level to hide that would buy tidiness in the picture and lose information in the stream.

type ValueStreamSummary

type ValueStreamSummary struct {
	Key         string   `json:"key"`
	Name        string   `json:"name"`
	Description string   `json:"description,omitempty"`
	Owner       Owner    `json:"owner"`
	Tags        []string `json:"tags,omitempty"`
	StageCount  int      `json:"stageCount"`
	// CapabilityCount is how many distinct capabilities the stages name — the
	// stream's breadth, which the stage count alone does not give.
	CapabilityCount int    `json:"capabilityCount"`
	KPICount        int    `json:"kpiCount"`
	ConfirmedAt     int64  `json:"confirmedAt,omitempty"`
	Stale           bool   `json:"stale"`
	Revision        int64  `json:"revision"`
	UpdatedAt       int64  `json:"updatedAt"`
	UpdatedBy       string `json:"updatedBy,omitempty"`
}

ValueStreamSummary is the list representation of a value stream.

type Window

type Window struct {
	From int64 `json:"from"`
	To   int64 `json:"to"`
}

Window is the span a walked reading covers, in Unix seconds.

It is required rather than optional, and that is the measurement's finding rather than a preference: an unwindowed reading over 100 000 finished instances cost 1.24 seconds, and per-phase duration 2.86. An endpoint that offers a number like that with no bound is offering a way to make somebody wait without telling them why.

func ParseWindow

func ParseWindow(days int, now time.Time) (Window, error)

ParseWindow reads the window a request asked for: a number of days back from now.

Days rather than a from/to pair, because every question this answers is of the form "over the last N days" and a pair invites the two failure modes a single number does not have — reversed ends, and a window whose age drifts as the clock moves while the caller believes it is fixed.

func (Window) Days

func (w Window) Days() int

Days is how many days the window spans, for a response that has to say what it measured rather than echo what was asked.

type Worker

type Worker struct {
	Ref     string `json:"ref"`
	Name    string `json:"name,omitempty"`
	Type    string `json:"type,omitempty"`
	CanView bool   `json:"-"`
}

Worker is one configured Worker: a target and identity of a Worker Type (ADR-0203).

Jump to

Keyboard shortcuts

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