journal

package
v0.4.2 Latest Latest
Warning

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

Go to latest
Published: Jun 29, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package journal is the append-only, crash-survivable event log that backs the session-handoff feature — the "episodic" typed view of agent state (events/history/decisions). Every state-mutating da command appends one typed Envelope (a single NDJSON line) on success, so a session killed mid-flight can be reconstructed from durable file state rather than re-grounded by replaying git/gh/da-status bursts.

Layout (D9)

The log lives OFF the git-tracked tree, under the XDG state directory, keyed by a stable per-repo fingerprint so one journal can span the several repos a single session touches (sweep/drift) without ever polluting a working tree:

<config.AgentsStateDir()>/journal/<repo-fingerprint>/
    events.log     append-only NDJSON, one Envelope per line  (this task)
    snapshot.json  PreCompact deterministic live-state snapshot (later task)
    reasoned.log   append-only reasoned-delta overlay          (later task)

This package owns the directory layout; later tasks add snapshot.json and reasoned.log writers alongside events.log.

Durability (D9 / R1)

A line append is a SINGLE os.Write of the full marshaled Envelope plus its trailing newline, performed under the package's interprocess advisory lock (agentslock.AcquireFileLock) so concurrent da processes can never interleave or tear each other's lines. Appends record what HAPPENED — a failed command emits a "failed" event, never a fabricated observed delta.

Index

Constants

View Source
const (
	CmdAdvance            = "workflow advance"
	CmdStartTask          = "workflow start-task"
	CmdCloseTask          = "workflow close-task"
	CmdPlanCreate         = "workflow plan create"
	CmdPlanArchive        = "workflow plan archive"
	CmdMergeBack          = "workflow merge-back"
	CmdFanout             = "workflow fanout"
	CmdFoldBackCreate     = "workflow fold-back create"
	CmdFoldBackUpdate     = "workflow fold-back update"
	CmdDelegationCloseout = "workflow delegation closeout"
	CmdContractCreate     = "workflow contract create"
	CmdPlanDeriveScope    = "workflow plan derive-scope"
	CmdVerifyRecord       = "workflow verify record"
	CmdCheckpoint         = "workflow checkpoint"
	CmdCommit             = "workflow commit"
	CmdArchiveOrphans     = "workflow archive-orphans"
	CmdSweepApply         = "workflow sweep --apply"

	CmdTaskAdd        = "workflow task add"
	CmdTaskUpdate     = "workflow task update"
	CmdPlanUpdate     = "workflow plan update"
	CmdPrefsSetLocal  = "workflow prefs set-local"
	CmdPrefsSetShared = "workflow prefs set-shared"

	CmdKGIngest          = "kg ingest"
	CmdKGLinkAdd         = "kg link add"
	CmdKGLinkRemove      = "kg link remove"
	CmdKGMaintainReweave = "kg maintain reweave"
	CmdKGMaintainStale   = "kg maintain mark-stale"
	CmdKGMaintainCompact = "kg maintain compact"
	CmdKGWarm            = "kg warm"

	CmdKGBuild       = "kg build"
	CmdKGUpdate      = "kg update"
	CmdKGPostprocess = "kg postprocess"

	CmdKGSync = "kg sync"

	CmdReviewApprove = "review approve"
	CmdReviewReject  = "review reject"
)

Canonical command names — the registry keys and the value the emit wiring passes. Hoisted to consts so the name appears exactly once (no scattered string literals) and the same identifier is shared by registry, emit, and recovery.

View Source
const (
	// SnapshotSchema namespaces the snapshot record so a reader can reject a
	// foreign file rather than guess. It is distinct from the event-log Schema.
	SnapshotSchema = "session-handoff-journal/snapshot"
	// SnapshotVersion is the snapshot schema version; readers gate on it. Bump it
	// when the captured shape changes incompatibly.
	SnapshotVersion = 1
)
View Source
const Schema = "session-handoff-journal/event"

Schema is the envelope schema name stamped on every event. It namespaces the journal's records within the broader event ecosystem and lets a reader reject foreign lines without guessing.

View Source
const Version = 1

Version is the envelope schema version. Bump it when the envelope's required shape changes incompatibly; readers gate on it.

Variables

This section is empty.

Functions

func Emit

func Emit(repoPath string, e Envelope) error

Emit appends one event to the journal for the repository at repoPath. It is the single append entrypoint commands call. The envelope's Schema, Version, TS, Seq, and (when empty) CwdRepo are stamped here; the caller supplies Actor, Command, EventType, and the payloads.

Durability (D9 / R1): the event is written as a SINGLE os.Write of the full NDJSON line (record + trailing newline) under the package interprocess lock, so concurrent da processes never interleave or tear lines. The journal directory is created on first append. Per R1 a failed event never carries an observed delta: Emit drops Observed when EventType is EventFailed. An event whose marshaled line exceeds maxEventBytes is rejected (the systemic no-bodies cap).

func EventsLogPath

func EventsLogPath(repoPath string) string

EventsLogPath returns the events.log path for the repository at repoPath.

func Fingerprint

func Fingerprint(repoPath string) string

Fingerprint returns the stable per-repo key used for the journal subdirectory. It prefers the trusted canonical repo identity (so the same logical repo maps to one journal across clones/worktrees on different paths) and falls back to a hash of the absolute checkout path when no portable identity is resolvable (e.g. a plain `git init`, a non-git directory, or an ambiguous fork origin). The two domains are prefixed before hashing so a repo id can never collide with a filesystem path that happens to equal it.

func IsJournaled

func IsJournaled(command string) bool

IsJournaled reports whether a command appends a journal event.

func RepoDir

func RepoDir(repoPath string) string

RepoDir returns the journal directory for the repository at repoPath: <AgentsStateDir>/journal/<fingerprint>. The directory is not created here; the appender MkdirAlls it before the first write.

func SnapshotPath

func SnapshotPath(repoPath string) string

SnapshotPath returns the snapshot.json path for the repository at repoPath: <journal-home>/snapshot.json.

Types

type Actor

type Actor string

Actor identifies which agent role appended the event (spec envelope → actor).

const (
	// ActorMain is the primary interactive session.
	ActorMain Actor = "main"
	// ActorLoopWorker is a bounded delegation worker.
	ActorLoopWorker Actor = "loop-worker"
	// ActorOrchestrator is the loop orchestrator above the workers.
	ActorOrchestrator Actor = "orchestrator"
)

type AdvanceInput

type AdvanceInput struct {
	Plan        string `json:"plan"`
	Task        string `json:"task"`
	Status      string `json:"status,omitempty"`
	CommitState string `json:"commit_state,omitempty"`
}

AdvanceInput is `workflow advance`'s invoked flags.

type AdvanceObserved

type AdvanceObserved struct {
	FromStatus string `json:"from_status"`
	ToStatus   string `json:"to_status"`
	Committed  bool   `json:"committed"`
	HeadSHA    string `json:"head_sha,omitempty"`
	Locus      *Locus `json:"locus,omitempty"`
}

AdvanceObserved is `workflow advance`'s durable delta.

func (AdvanceObserved) ValidateLocus

func (o AdvanceObserved) ValidateLocus() error

ValidateLocus implements locusCarrier for the task-transition observed deltas. A nil embedded Locus is valid (Locus.Validate handles it); a present Locus must be a well-formed sum type.

type ArchiveOrphansInput

type ArchiveOrphansInput struct {
	DryRun bool `json:"dry_run,omitempty"`
}

ArchiveOrphansInput is `workflow archive-orphans`'s invoked flags.

type ArchiveOrphansObserved

type ArchiveOrphansObserved struct {
	Actions []OrphanAction `json:"actions,omitempty"`
}

ArchiveOrphansObserved is `workflow archive-orphans`'s durable delta.

type CanonicalRef

type CanonicalRef struct {
	Ref string `json:"ref"`
}

CanonicalRef is a landed-on-canonical-branch reference, e.g. ref "master@<sha>".

type ChangedFields

type ChangedFields []FieldDelta

ChangedFields is the bounded set of fields a Tier-2 command changed. It is a slice of FieldDelta (metadata only) rather than a name→value map, and FieldDelta has no exported field, so a body cannot be smuggled into the journal even by a careless emitter (R6/D4). Build it only via NewChangedFields.

func NewChangedFields

func NewChangedFields(raw map[string]string) (ChangedFields, error)

NewChangedFields reduces a name→raw-value map to bounded per-field metadata (length + SHA-256 prefix), discarding the raw values. Output is sorted by field name for a deterministic record. An empty input yields a nil ChangedFields.

It returns an error if any key is empty or longer than maxFieldNameLen: a Tier-2 field name is always a short identifier, so an over-long key is invalid input, not something to record — truncating it would corrupt the field name and silently dropping it would hide a body, so the only correct response is to fail. This makes name as un-stuffable as sha256 (every FieldDelta string is bounded).

type CheckpointInput

type CheckpointInput struct {
	Message            string `json:"message"`
	VerificationStatus string `json:"verification_status,omitempty"`
	LogToIter          bool   `json:"log_to_iter,omitempty"`
}

CheckpointInput is `workflow checkpoint`'s invoked flags.

type CheckpointObserved

type CheckpointObserved struct {
	CheckpointID string `json:"checkpoint_id"`
	IterStubPath string `json:"iter_stub_path,omitempty"`
}

CheckpointObserved is `workflow checkpoint`'s durable delta.

type CloseTaskInput

type CloseTaskInput struct {
	Plan      string `json:"plan"`
	Task      string `json:"task"`
	NextFocus string `json:"next_focus,omitempty"`
}

CloseTaskInput is `workflow close-task`'s invoked flags.

type CloseTaskObserved

type CloseTaskObserved struct {
	ToStatus     string `json:"to_status"`
	NextFocusSet bool   `json:"next_focus_set"`
	Committed    bool   `json:"committed"`
	HeadSHA      string `json:"head_sha,omitempty"`
	Locus        *Locus `json:"locus,omitempty"`
}

CloseTaskObserved is `workflow close-task`'s durable delta.

func (CloseTaskObserved) ValidateLocus

func (o CloseTaskObserved) ValidateLocus() error

ValidateLocus implements locusCarrier for CloseTaskObserved.

type CommandSpec

type CommandSpec struct {
	// contains filtered or unexported fields
}

CommandSpec is one registry entry: the canonical command, its Tier, and factories that allocate fresh typed Input/Observed values. The factories let the recovery view decode an event's opaque payloads back into the right type without a type switch, and let tests round-trip every schema generically.

func Lookup

func Lookup(command string) (CommandSpec, bool)

Lookup returns the registry entry for a canonical command name. The second return is false when the command is not journaled (the spec's Excluded set).

func (CommandSpec) Command

func (s CommandSpec) Command() string

Command is the canonical command name.

func (CommandSpec) EventType

func (s CommandSpec) EventType() EventType

EventType is the envelope event_type the emit wiring stamps for this command: a Tier-2 delta is input_only (observed carries only changed fields); every other tier records a durable_delta. A failed invocation overrides this with EventFailed via NewFailedEvent.

func (CommandSpec) NewInput

func (s CommandSpec) NewInput() any

NewInput allocates a fresh, zero-valued pointer to this command's Input type.

func (CommandSpec) NewObserved

func (s CommandSpec) NewObserved() any

NewObserved allocates a fresh, zero-valued pointer to this command's Observed type.

func (CommandSpec) Tier

func (s CommandSpec) Tier() Tier

Tier is the command's tier classification.

type CommitInput

type CommitInput struct {
	Includes []string `json:"includes,omitempty"`
	DryRun   bool     `json:"dry_run,omitempty"`
}

CommitInput is `workflow commit`'s invoked flags.

type CommitObserved

type CommitObserved struct {
	StagedPaths []string `json:"staged_paths,omitempty"`
	HeadSHA     string   `json:"head_sha,omitempty"`
	Noop        bool     `json:"noop"`
}

CommitObserved is `workflow commit`'s durable delta.

type ContractCreateInput

type ContractCreateInput struct {
	Plan       string   `json:"plan"`
	Task       string   `json:"task"`
	Owner      string   `json:"owner,omitempty"`
	WriteScope []string `json:"write_scope,omitempty"`
	Mode       string   `json:"mode,omitempty"`
	Force      bool     `json:"force,omitempty"`
}

ContractCreateInput is `workflow contract create`'s invoked flags. Sibling of fanout: it persists a delegation contract for direct/delegated orchestrator work.

type ContractCreateObserved

type ContractCreateObserved struct {
	ContractID         string   `json:"contract_id"`
	Mode               string   `json:"mode"`
	Status             string   `json:"status,omitempty"`
	ContractPath       string   `json:"contract_path,omitempty"`
	ResolvedWriteScope []string `json:"resolved_write_scope,omitempty"`
}

ContractCreateObserved is `workflow contract create`'s durable delta: the persisted contract's id/mode/status/path and the resolved write scope.

type DelegationCloseoutInput

type DelegationCloseoutInput struct {
	Plan     string `json:"plan"`
	Task     string `json:"task"`
	Decision string `json:"decision"`
	Note     string `json:"note,omitempty"`
}

DelegationCloseoutInput is `workflow delegation closeout`'s invoked flags.

type DelegationCloseoutObserved

type DelegationCloseoutObserved struct {
	ArchivedPaths        []string `json:"archived_paths,omitempty"`
	ReconciledTaskStatus string   `json:"reconciled_task_status"`
}

DelegationCloseoutObserved is `workflow delegation closeout`'s durable delta.

type DelegationState

type DelegationState struct {
	ID           string `json:"id"`
	ParentPlanID string `json:"parent_plan_id"`
	ParentTaskID string `json:"parent_task_id"`
	Status       string `json:"status"`
}

DelegationState is one in-flight delegation: the parent plan/task it serves and its status. Bounded — no write scope, prompt, or summary body.

type DeltaInput

type DeltaInput struct {
	Plan          string        `json:"plan,omitempty"`
	Task          string        `json:"task,omitempty"`
	ChangedFields ChangedFields `json:"changed_fields,omitempty"`
}

DeltaInput is the shared invoked-flags shape for the field-replacing Tier-2 commands (task update, plan update, prefs set-local/set-shared).

type DeltaObserved

type DeltaObserved struct {
	FieldsReplaced []string `json:"fields_replaced,omitempty"`
}

DeltaObserved is the shared durable delta for the field-replacing Tier-2 commands: the names of the fields actually replaced (no-ops record nothing).

type DeriveScopeInput

type DeriveScopeInput struct {
	Plan        string   `json:"plan"`
	Task        string   `json:"task"`
	SeedSymbols []string `json:"seed_symbols,omitempty"`
	SeedPaths   []string `json:"seed_paths,omitempty"`
}

DeriveScopeInput is `workflow plan derive-scope`'s invoked flags.

type DeriveScopeObserved

type DeriveScopeObserved struct {
	SidecarPath   string `json:"sidecar_path"`
	Mode          string `json:"mode,omitempty"`
	Confidence    string `json:"confidence,omitempty"`
	RequiredPaths int    `json:"required_paths"`
	Queries       int    `json:"queries"`
}

DeriveScopeObserved is `workflow plan derive-scope`'s durable delta: where the scope-evidence sidecar was written and a summary of what was derived — counts and the confidence label, not the evidence bodies (D4).

type Envelope

type Envelope struct {
	// Schema/Version namespace and version the record (stamped by Emit).
	Schema  string `json:"schema"`
	Version int    `json:"version"`

	// TS is the RFC3339 UTC nanosecond timestamp — the primary replay ordering
	// key. Stamped by Emit when empty.
	TS string `json:"ts"`
	// Seq is a process-monotonic counter, the tiebreaker for records sharing a
	// TS. Stamped by Emit.
	Seq int64 `json:"seq"`

	// Actor is the agent role that produced the event.
	Actor Actor `json:"actor"`
	// Command is the canonical command name, e.g. "workflow advance".
	Command string `json:"command"`
	// CwdRepo is the repo identity/path the command ran against. The journal may
	// span repos (sweep/drift). Stamped by Emit from the repo fingerprint when
	// the caller leaves it empty.
	CwdRepo string `json:"cwd_repo"`
	// EventType selects durable_delta / input_only / failed.
	EventType EventType `json:"event_type"`

	// Input is the typed flags the command was invoked with (opaque here).
	Input json.RawMessage `json:"input,omitempty"`
	// Observed is the typed durable delta the command produced (opaque here).
	// Always absent on an EventFailed record (R1: never a false observed).
	Observed json.RawMessage `json:"observed,omitempty"`
}

Envelope is one journal record: a single JSON object serialized to one NDJSON line in events.log. It is the common shape every journaled command shares (spec "Common envelope"). The per-command typed Input/Observed schemas land in a later task; here Input and Observed stay json.RawMessage so callers can carry any payload and the later task can specialize without reshaping the envelope.

Schema/Version/TS/Seq are stamped by Emit; callers set Actor, Command, EventType, and the payloads. Marshaling is plain encoding/json — no custom MarshalJSON — so a round-trip through json is lossless and order-stable enough for NDJSON.

func NewEvent

func NewEvent(command string, actor Actor, input, observed any) (Envelope, error)

NewEvent builds an Envelope for a journaled command from its typed input and observed payloads, stamping the EventType from the command's tier. It is the bridge the emit wiring uses to carry typed payloads through the opaque envelope: the typed values are marshaled into the envelope's RawMessage fields and tagged with the command. A nil (or typed-nil) payload is omitted. The remaining envelope fields (schema, version, ts, seq, actor, cwd_repo) are stamped by Emit.

The typed registry is the CONTRACT, not a suggestion: input and observed must be exactly the registered spec types for the command — *AdvanceInput / *AdvanceObserved for "workflow advance", etc. (or nil to omit a payload). A caller cannot hand NewEvent a json.RawMessage(arbitraryJSON) or a wrong struct to bypass the schemas; a type mismatch is rejected. This is what makes the per-command schemas the real shape of every typed event.

It returns an error for a command that is not journaled, for a payload whose type does not match the registered schema, and for an observed payload whose Locus is invalid (R8 sum-type: exactly one arm) — so a miswired caller fails loudly at construction rather than persisting an off-schema or ambiguous event.

func NewFailedEvent

func NewFailedEvent(command string, actor Actor, input any) (Envelope, error)

NewFailedEvent builds an EventFailed envelope for a journaled command that did NOT succeed. Per R1 a failure never carries a fabricated observed delta, so only the invoked input is recorded; observed is always absent. The input is schema-typed exactly as in NewEvent.

func (Envelope) MarshalLine

func (e Envelope) MarshalLine() ([]byte, error)

MarshalLine serializes the envelope to a single NDJSON line: the compact JSON object followed by exactly one '\n', returned as one buffer so the appender can write it in a single os.Write (no torn lines). json.Marshal already emits no interior newlines for these field types, keeping the record one line.

type EventType

type EventType string

EventType marks how much of the command's effect a record carries (spec "Common envelope" → event_type).

const (
	// EventDurableDelta is a command that produced a durable, replayable state
	// transition; observed carries the full typed delta (Tier-1).
	EventDurableDelta EventType = "durable_delta"
	// EventInputOnly is a command whose durable effect is recoverable from the
	// snapshot or an underlying file, so only the invoked input is recorded
	// (Tier-2 records observed:{changed_fields}; some commands record input only).
	EventInputOnly EventType = "input_only"
	// EventFailed records that an attempted command did NOT succeed. Per R1 the
	// journal never fabricates an observed delta for a failure; a failed event
	// carries the input and the reason, never a false observed.
	EventFailed EventType = "failed"
)

type FanoutInput

type FanoutInput struct {
	Plan             string   `json:"plan"`
	Task             string   `json:"task"`
	WriteScope       []string `json:"write_scope,omitempty"`
	DelegateProfile  string   `json:"delegate_profile,omitempty"`
	BaseBranch       string   `json:"base_branch,omitempty"`
	VerifierSequence []string `json:"verifier_sequence,omitempty"`
}

FanoutInput is `workflow fanout`'s invoked flags.

type FanoutObserved

type FanoutObserved struct {
	DelegationPath     string   `json:"delegation_path"`
	BundlePath         string   `json:"bundle_path"`
	ResolvedBaseBranch string   `json:"resolved_base_branch,omitempty"`
	ResolvedWriteScope []string `json:"resolved_write_scope,omitempty"`
}

FanoutObserved is `workflow fanout`'s durable delta.

type FieldDelta

type FieldDelta struct {
	// contains filtered or unexported fields
}

FieldDelta is the bounded record of one changed Tier-2 field: its name plus metadata about the new value — its byte length and a short SHA-256 prefix — but NEVER the value itself (spec D4: Tier-2 journals the delta, not bodies).

Its fields are UNEXPORTED so the no-bodies invariant is structural, not by convention: no caller in any other package can construct a FieldDelta literal and stuff a raw value into it. The only populating path is NewChangedFields, which hashes and discards the raw value. The wire format (name/len/sha256) is preserved by the custom Marshal/UnmarshalJSON, and the values are exposed read-only via Name/Len/SHA256 so the recovery view can re-verify against the canonical file (TASKS.yaml/PLAN.yaml/prefs).

func (FieldDelta) Len

func (f FieldDelta) Len() int

Len is the byte length of the new value.

func (FieldDelta) MarshalJSON

func (f FieldDelta) MarshalJSON() ([]byte, error)

MarshalJSON emits the stable name/len/sha256 wire shape.

func (FieldDelta) Name

func (f FieldDelta) Name() string

Name is the changed field's name.

func (FieldDelta) SHA256

func (f FieldDelta) SHA256() string

SHA256 is the short SHA-256 prefix of the new value (changedFieldHashLen hex chars), used to detect whether the journaled change is still the current value.

func (*FieldDelta) UnmarshalJSON

func (f *FieldDelta) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the wire shape back into the unexported fields, so the recovery view can read a journaled FieldDelta without a re-export. Crucially it VALIDATES the bounded shape on decode: the decode path is a second population route that would otherwise let a raw body masquerade as the sha256 field, so a decoded FieldDelta must look exactly like one NewChangedFields produces — a fixed-length lowercase-hex digest, a non-negative length, and a present name — or the unmarshal fails. This keeps the no-bodies invariant structural across BOTH construction and deserialization.

type FoldBackInput

type FoldBackInput struct {
	Plan        string `json:"plan"`
	Task        string `json:"task,omitempty"`
	Observation string `json:"observation"`
	Slug        string `json:"slug,omitempty"`
	Propose     bool   `json:"propose,omitempty"`
}

FoldBackInput is `workflow fold-back create`/`update`'s invoked flags.

type FoldBackObserved

type FoldBackObserved struct {
	ArtifactID string   `json:"artifact_id"`
	RoutedTo   []string `json:"routed_to,omitempty"`
	Action     string   `json:"action"`
}

FoldBackObserved is `workflow fold-back create`/`update`'s durable delta.

type Identity

type Identity struct {
	Fingerprint   string `json:"fingerprint"`
	RepoID        string `json:"repo_id,omitempty"`
	RemoteURL     string `json:"remote_url,omitempty"`
	DefaultBranch string `json:"default_branch,omitempty"`
	CurrentBranch string `json:"current_branch,omitempty"`
	Head          string `json:"head,omitempty"`
	WorktreePath  string `json:"worktree_path,omitempty"`
	PRNumber      int    `json:"pr_number,omitempty"`
}

Identity is the D8 composite key that uniquely pins a journal entry to a repo state, so read-back can quarantine a handoff captured against a different remote/branch/HEAD than the resuming session (spec D8 — "a wrong handoff is worse than none").

ResolveIdentity populates the in-process, locally-knowable fields (Fingerprint, RepoID, RemoteURL, WorktreePath). The live git-state fields (DefaultBranch, CurrentBranch, Head, PRNumber) are part of the keying tuple but are populated by the verified-recovery layer (a later task), which already runs the git/gh reads required to determine and re-verify them; they stay zero-value here and omitempty out of the serialized key until then.

func ResolveIdentity

func ResolveIdentity(repoPath string) Identity

ResolveIdentity builds the locally-knowable portion of the composite identity for the checkout at repoPath. It never fails: a non-git or unidentifiable path still yields a usable Identity carrying the path-derived Fingerprint and the absolute WorktreePath, with the git-derived fields left blank.

type InOpenPRRef

type InOpenPRRef struct {
	PR     int    `json:"pr"`
	Status string `json:"status"`
}

InOpenPRRef is a lives-in-an-open-PR reference: the PR number plus the task's status within that PR.

type KGContentDeltaInput

type KGContentDeltaInput struct {
	Operation string   `json:"operation"`
	Targets   []string `json:"targets,omitempty"`
}

KGContentDeltaInput is the shared invoked-flags shape for the KG content-delta commands (link add/remove, maintain reweave|mark-stale|compact, warm). Operation names the concrete sub-command; Targets are the file/note/link ids the invocation named.

type KGContentDeltaObserved

type KGContentDeltaObserved struct {
	Counts map[string]int `json:"counts,omitempty"`
	IDs    []string       `json:"ids,omitempty"`
}

KGContentDeltaObserved is the shared durable delta for KG content-delta commands: counts keyed by what changed plus the affected ids — ids not bodies.

type KGDecisionInput

type KGDecisionInput struct {
	Repo string `json:"repo,omitempty"`
	Base string `json:"base,omitempty"`
}

KGDecisionInput is the shared invoked-flags shape for the KG decision-event commands (build, update, postprocess).

type KGDecisionObserved

type KGDecisionObserved struct {
	Outcome string `json:"outcome"`
	Nodes   *int   `json:"nodes,omitempty"`
	Edges   *int   `json:"edges,omitempty"`
	Files   *int   `json:"files,omitempty"`
}

KGDecisionObserved is the shared durable delta for KG decision-event commands: the outcome plus optional graph counts — "rebuilt at base X", never node/edge dumps.

type KGIngestInput

type KGIngestInput struct {
	File string `json:"file,omitempty"`
	All  bool   `json:"all,omitempty"`
	Type string `json:"type,omitempty"`
}

KGIngestInput is `kg ingest`'s invoked flags.

type KGIngestObserved

type KGIngestObserved struct {
	NotesCreated int      `json:"notes_created"`
	NotesUpdated int      `json:"notes_updated"`
	NoteIDs      []string `json:"note_ids,omitempty"`
}

KGIngestObserved is `kg ingest`'s durable delta — counts + ids, not bodies.

type KGSyncInput

type KGSyncInput struct {
	Push bool `json:"push,omitempty"`
}

KGSyncInput is `kg sync`'s invoked flags.

type KGSyncObserved

type KGSyncObserved struct {
	PullStatus string `json:"pull_status,omitempty"`
	PushStatus string `json:"push_status,omitempty"`
	HeadSHA    string `json:"head_sha,omitempty"`
}

KGSyncObserved is `kg sync`'s durable delta. kg sync moves a git remote and is not locally snapshot-recoverable, so it is journaled fully.

type Locus

type Locus struct {
	// Canonical is set when the change is landed on the canonical branch.
	Canonical *CanonicalRef `json:"canonical,omitempty"`
	// InOpenPR is set when the change lives only in an open, unmerged PR.
	InOpenPR *InOpenPRRef `json:"in_open_pr,omitempty"`
}

Locus pins a tracked item to where its state currently lives (spec R8): a task can be "done in an open PR, not yet merged". Recovery must not conflate in-PR work with done-on-master or with fresh-eligible work, so the observed delta of a task-transition command carries the item's locus. Exactly one side is set.

func (*Locus) Validate

func (l *Locus) Validate() error

Validate enforces Locus as a sum type: exactly one arm must be set. An absent (nil) Locus is valid — many transitions carry no locus — but a present Locus with both or neither arm set is rejected, so a caller can never produce a record that conflates in-PR with done-on-master (spec R8). NewEvent calls this at construction time, failing loudly rather than persisting an ambiguous locus.

type MergeBackInput

type MergeBackInput struct {
	Task               string `json:"task"`
	Summary            string `json:"summary,omitempty"`
	VerificationStatus string `json:"verification_status,omitempty"`
	CommitState        string `json:"commit_state,omitempty"`
}

MergeBackInput is `workflow merge-back`'s invoked flags.

type MergeBackObserved

type MergeBackObserved struct {
	ArtifactPath string   `json:"artifact_path"`
	FilesChanged []string `json:"files_changed,omitempty"`
	Verdict      string   `json:"verdict,omitempty"`
	Committed    bool     `json:"committed"`
	HeadSHA      string   `json:"head_sha,omitempty"`
	Locus        *Locus   `json:"locus,omitempty"`
}

MergeBackObserved is `workflow merge-back`'s durable delta.

func (MergeBackObserved) ValidateLocus

func (o MergeBackObserved) ValidateLocus() error

ValidateLocus implements locusCarrier for MergeBackObserved.

type OrphanAction

type OrphanAction struct {
	Artifact   string `json:"artifact"`
	Class      string `json:"class"`
	Resolution string `json:"resolution"`
	DestPath   string `json:"dest_path,omitempty"`
}

OrphanAction is one resolution within an `archive-orphans` run.

type PlanArchiveInput

type PlanArchiveInput struct {
	Plans []string `json:"plans"`
	Force bool     `json:"force,omitempty"`
}

PlanArchiveInput is `workflow plan archive`'s invoked flags.

type PlanArchiveObserved

type PlanArchiveObserved struct {
	ArchivePaths      []string `json:"archive_paths,omitempty"`
	ActiveDirsRemoved []string `json:"active_dirs_removed,omitempty"`
}

PlanArchiveObserved is `workflow plan archive`'s durable delta.

type PlanCreateInput

type PlanCreateInput struct {
	Plan  string `json:"plan"`
	Title string `json:"title,omitempty"`
	Owner string `json:"owner,omitempty"`
}

PlanCreateInput is `workflow plan create`'s invoked flags.

type PlanCreateObserved

type PlanCreateObserved struct {
	PlanDir      string   `json:"plan_dir"`
	FilesCreated []string `json:"files_created,omitempty"`
}

PlanCreateObserved is `workflow plan create`'s durable delta.

type PlanState

type PlanState struct {
	ID               string      `json:"id"`
	Status           string      `json:"status"`
	CurrentFocusTask string      `json:"current_focus_task,omitempty"`
	Tasks            []TaskState `json:"tasks"`
}

PlanState is one active plan's live state: its id, status, current focus task, and its tasks (sorted by id for a canonical, order-independent capture).

type ReviewInput

type ReviewInput struct {
	ProposalID string `json:"proposal_id"`
	Reason     string `json:"reason,omitempty"`
}

ReviewInput is `review approve`/`reject`'s invoked flags.

type ReviewObserved

type ReviewObserved struct {
	Decision         string `json:"decision"`
	Applied          bool   `json:"applied"`
	ArchivedPath     string `json:"archived_path,omitempty"`
	RefreshTriggered bool   `json:"refresh_triggered"`
}

ReviewObserved is `review approve`/`reject`'s durable delta.

type SnapshotState

type SnapshotState struct {
	// Schema/Version namespace and version the record.
	Schema  string `json:"schema"`
	Version int    `json:"version"`
	// CapturedAt is the RFC3339 UTC nanosecond capture time (the one varying
	// field; everything else is a pure function of the captured state).
	CapturedAt string `json:"captured_at"`
	// Identity pins the snapshot to the repo it was captured against, so a
	// recovery can quarantine a snapshot taken against a different checkout.
	Identity Identity `json:"identity"`
	// Plans is the captured active plans, sorted by id.
	Plans []PlanState `json:"plans"`
	// PendingUnblocked is a deterministic, filesystem-only PROJECTION: pending
	// tasks whose declared dependencies are all satisfied, sorted by (plan,
	// task). It is deliberately weaker than — and NOT a substitute for — the
	// authoritative `da workflow eligible` engine, which additionally considers
	// in_progress tasks, filters to active plans, excludes tasks under an active
	// delegation lock, and falls back to .agents/history for cross-plan deps.
	// Re-implementing that here would duplicate the engine and drift, so the
	// snapshot records only this cheap, byte-stable hint; p5 recovery should
	// call `da workflow eligible` directly for authoritative eligibility.
	PendingUnblocked []TaskRef `json:"pending_unblocked"`
	// Delegations is the in-flight delegations (pending/active), sorted by id.
	Delegations []DelegationState `json:"delegations"`
	// PendingMergeBacks is the task ids with a merge-back awaiting integration,
	// sorted.
	PendingMergeBacks []string `json:"pending_merge_backs"`
}

SnapshotState is the deterministic capture of current workflow state written to <journal-home>/snapshot.json. It is the value the Snapshot capture function returns and the LoadSnapshot read path decodes. The type is named distinctly from the Snapshot function (Go forbids a type and func sharing a name) while keeping the capture entrypoint the plan named: journal.Snapshot(repoPath).

func LoadSnapshot

func LoadSnapshot(repoPath string) (SnapshotState, error)

LoadSnapshot reads and decodes the snapshot for the repository at repoPath. It is the read path p5 (recovery view) and p7 (PreCompact hook) use to re-inject state. A missing file surfaces the os.ReadFile error; malformed JSON is wrapped.

func Snapshot

func Snapshot(repoPath string) (SnapshotState, error)

Snapshot captures the current workflow state for the repository at repoPath and writes it to <journal-home>/snapshot.json, returning the captured value. The capture itself never fails — a missing or unreadable workflow tree yields an empty-but-valid snapshot, mirroring ResolveIdentity's "never fails" contract — so the only error paths are the marshal/mkdir/lock/write of the persisted file.

type StartTaskInput

type StartTaskInput struct {
	Plan        string   `json:"plan"`
	Task        string   `json:"task"`
	SeedSymbols []string `json:"seed_symbols,omitempty"`
	SeedPaths   []string `json:"seed_paths,omitempty"`
}

StartTaskInput is `workflow start-task`'s invoked flags.

type StartTaskObserved

type StartTaskObserved struct {
	ToStatus    string `json:"to_status"`
	SidecarPath string `json:"sidecar_path,omitempty"`
	Committed   bool   `json:"committed"`
	HeadSHA     string `json:"head_sha,omitempty"`
}

StartTaskObserved is `workflow start-task`'s durable delta.

type SweepApplyInput

type SweepApplyInput struct {
	StaleDays    int `json:"stale_days,omitempty"`
	ProposalDays int `json:"proposal_days,omitempty"`
}

SweepApplyInput is `workflow sweep --apply`'s invoked flags.

type SweepApplyObserved

type SweepApplyObserved struct {
	FixesApplied []SweepFix `json:"fixes_applied,omitempty"`
}

SweepApplyObserved is `workflow sweep --apply`'s durable delta.

type SweepFix

type SweepFix struct {
	Project string `json:"project"`
	Action  string `json:"action"`
}

SweepFix is one applied fix within a `sweep --apply` run.

type TaskAddInput

type TaskAddInput struct {
	Plan       string   `json:"plan"`
	TaskID     string   `json:"task_id"`
	Title      string   `json:"title,omitempty"`
	DependsOn  []string `json:"depends_on,omitempty"`
	WriteScope []string `json:"write_scope,omitempty"`
	AppType    string   `json:"app_type,omitempty"`
}

TaskAddInput is `workflow task add`'s invoked flags.

type TaskAddObserved

type TaskAddObserved struct {
	Appended bool `json:"appended"`
}

TaskAddObserved is `workflow task add`'s durable delta.

type TaskRef

type TaskRef struct {
	Plan string `json:"plan"`
	Task string `json:"task"`
}

TaskRef names a single task within a plan — the bounded identity recovery uses to re-verify a task without carrying its body.

type TaskState

type TaskState struct {
	ID        string   `json:"id"`
	Status    string   `json:"status"`
	DependsOn []string `json:"depends_on,omitempty"`
	Locus     *Locus   `json:"locus,omitempty"`
}

TaskState is one task's live, bounded state: its id, status, dependency edges, and the locus that records whether its work is landed on the canonical branch or lives in an open PR (nil when the status implies neither).

type Tier

type Tier string

Tier classifies a journaled command by how much of its effect the record carries (spec "Command Surface" Tiers 1–2 + KG + review). The Tier selects the envelope EventType the emit wiring stamps.

const (
	// TierUnconditional (spec Tier 1) is a canonical state transition or
	// irreversible filesystem move: it journals every time with the full typed
	// observed delta. Maps to EventDurableDelta.
	TierUnconditional Tier = "tier-1"
	// TierDelta (spec Tier 2) journals only when it changed something; the full
	// content is re-readable from TASKS.yaml/PLAN.yaml/prefs, so observed carries
	// only the changed fields (R6). Maps to EventInputOnly.
	TierDelta Tier = "tier-2"
	// TierKG covers the KG commands: content deltas record counts + ids and
	// decision events record outcomes — never node/edge bodies (D4). These are
	// durable transitions, so they map to EventDurableDelta.
	TierKG Tier = "kg"
	// TierReview covers review approve/reject: the decision outcome is a durable
	// transition. Maps to EventDurableDelta.
	TierReview Tier = "review"
)

type VerifyRecordInput

type VerifyRecordInput struct {
	Kind    string `json:"kind"`
	Status  string `json:"status"`
	Scope   string `json:"scope,omitempty"`
	Summary string `json:"summary,omitempty"`
	Task    string `json:"task,omitempty"`
}

VerifyRecordInput is `workflow verify record`'s invoked flags.

type VerifyRecordObserved

type VerifyRecordObserved struct {
	VerificationLogID  string `json:"verification_log_id"`
	ResultArtifactPath string `json:"result_artifact_path,omitempty"`
}

VerifyRecordObserved is `workflow verify record`'s durable delta.

Jump to

Keyboard shortcuts

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