identity

package
v0.33.0 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

README

pkg/identity

pkg/identity holds the shared correlation types used across the loop, command, and event packages: the Coordinates quartet, the Cause causal edge, and the Agency audit enum. It sits below pkg/event and pkg/command and imports only github.com/looprig/core/uuid, so embedding these types never forms an import cycle.

What is identity?

  • Coordinates — the four nesting ids that locate any action in the hierarchy: SessionID ▸ LoopID ▸ TurnID ▸ StepID. Declared once and embedded wherever the full quartet is needed (event.Header, Cause, GateRoute). Named Coordinates — not Scope — because event.Scope is already the delivery-scope enum.
  • AgentName — the immutable attribution name a loop runs under (e.g. "operator", "code reviewer"). Stamped on the loop's LoopStarted at creation and never changes for the life of the loop, so the durable record carries a stable answer to "which agent produced this?". The zero value (empty string) means UNSET — a plain loop started without an attribution name, or a record persisted before AgentName existed.
  • AgencyAgencyMachine (the fail-secure default: "our code did it") or AgencyUser ("a human did it"). It is an audit/observability record per action (who approved, who interrupted), not the gate decision. The zero value is AgencyMachine so a missing value never falsely claims a human acted.
  • Cause — the direct causal edge: "the thing that caused this". Carried in the same id vocabulary as Coordinates, plus a CommandID, EventID, ToolExecutionID, and a copy of the causing command's Agency so an event can surface agency without chasing the command. Most fields are omitted per event/command; only the set ones are populated.

How to use

You usually don't construct these directly — the runtime stamps them on every event and command. You read them when you handle an event:

for delivery := range sub.Events() {
    header := delivery.Event.EventHeader()
    sid := header.Coordinates.SessionID
    lid := header.Coordinates.LoopID
    tid := header.Coordinates.TurnID
    cmdID := header.Cause.CommandID   // the command that caused this event
    who  := header.Cause.Agency        // AgencyMachine by default; AgencyUser if a human
    _ = sid; _ = lid; _ = tid; _ = cmdID; _ = who
}

A loop.Define names a loop with an AgentName, and that name is the attribution stamped on every event the loop emits:

operator, _ := loop.Define(
    loop.WithName(identity.AgentName("operator")),
    /* ... */
)

Restore treats an empty stored AgentName as distinct from a configured non-empty one — it does not silently accept the legacy zero as a match — so a name change is never resumed unnoticed.

Sibling packages

  • pkg/event — embeds Coordinates and Cause in event.Header; uses Agency for audit.
  • pkg/command — embeds them in command.Header and command.GateRoute.
  • pkg/looploop.WithName takes an AgentName; loop.WithDelegates takes []AgentName.
  • pkg/hub — the hub's event.Factory stamps Header on synthesized session events.
  • pkg/journal — records carry Coordinates for routing.

How it is designed

                   identity (this package)
                          │
          ┌───────────────┼───────────────┐
          │               │               │
          ▼               ▼               ▼
   pkg/event        pkg/command       pkg/loop
   (Header)         (Header, GateRoute) (WithName, WithDelegates)
          │
          │  imports only core/uuid
          ▼
       no cycle

The package is intentionally a leaf: it depends only on core/uuid. Every consumer embeds its types rather than re-declaring them, so the correlation vocabulary is one set of fields, named once, and an Agency value is always the same uint8 no matter which package reads it.

Why Agency defaults to Machine

A missing audit value should never be mistaken for "a human did it". AgencyMachine = 0 is the fail-secure default: a record that fails to set Agency reads as "our code did it", never as a forged human attribution. AgencyUser is set explicitly by the path that handled a human's action (e.g. the session setting it on an Interrupt command constructed from an HTTP POST /interrupt).

Why AgentName is a named type, not a bare string

A loop's attribution name has domain meaning; bare strings lose that. The named type is also the restore-comparison key: a stored empty name is treated as distinct from a configured non-empty one, which only reads correctly if the two are the same named type all the way through the journal, the comparison, and the loop definition.

Documentation

Overview

Package identity holds the shared correlation types used across the loop, command, and event packages: the Coordinates quartet, the Cause causal edge, and the Agency audit enum. It sits below event/command and imports only internal/uuid, so embedding these types never forms an import cycle.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Agency

type Agency uint8

Agency records who performed an action — per action, not per turn. It is an audit/observability record (who approved, who interrupted), not the gate decision. The zero value is AgencyMachine, the fail-secure default: a missing value reads as "our code did it", so we never falsely claim a human acted.

const (
	AgencyMachine Agency = iota // 0 — the DEFAULT: our code did it
	AgencyUser                  // a human did it
)

func (Agency) String

func (a Agency) String() string

String renders the agency for logs. Unknown values render as Agency(n).

type AgentName

type AgentName string

AgentName is the immutable attribution name a loop runs under — the role/identity of the agent driving that loop (e.g. "operator", "code reviewer"). It is stamped on the loop's LoopStarted at creation and never changes for the life of the loop, so the durable record carries a stable answer to "which agent produced this?".

The zero value (the empty string) means UNSET: a plain loop that was started without an attribution name, or a record persisted before AgentName existed. Restore treats an empty stored name as distinct from a configured non-empty one (it does not silently accept the legacy zero as a match) so a name change is never resumed unnoticed.

type Cause

type Cause struct {
	Coordinates               // the cause's coordinates (full quartet, mostly omitted)
	CommandID       uuid.UUID `json:"command_id,omitzero"`
	EventID         uuid.UUID `json:"event_id,omitzero"`
	ToolExecutionID uuid.UUID `json:"tool_execution_id,omitzero"`
	Agency          Agency    `json:"agency,omitzero"` // the causing command's agency (machine by default)
}

Cause is the direct causal edge — "the thing that caused this" — expressed in the same id vocabulary. Which fields are set varies per event/command; most are omitted. Agency is a copy of the causing command's Header.Agency, carried so an event can surface agency without chasing the command.

type Coordinates

type Coordinates struct {
	SessionID uuid.UUID `json:"session_id,omitzero"`
	LoopID    uuid.UUID `json:"loop_id,omitzero"`
	TurnID    uuid.UUID `json:"turn_id,omitzero"`
	StepID    uuid.UUID `json:"step_id,omitzero"`
}

Coordinates is the four nesting ids that locate any action in the hierarchy: session ▸ loop ▸ turn ▸ step. Declared once and embedded wherever the full quartet is needed (event.Header, Cause, GateRoute). Named Coordinates — not Scope — because event.Scope is already the delivery-scope enum.

Jump to

Keyboard shortcuts

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