Documentation
¶
Overview ¶
Package ledger is the authoritative, on-disk record of a run: run.json holds the engine's current State (what resume loads directly — never by replaying internal/sdlc/journal's observability log), and nodes/<id>.json holds each node's own execution history for audit and `sdlc status`. Every write is atomic: a same-directory temp file plus rename, guarded by an exclusive lock on a dedicated lock file (internal/filelock), so a crash mid-write can never leave a reader with a partially-written file, and concurrent writers can never interleave.
Index ¶
- Constants
- type Activity
- type Allowances
- type AttemptRecord
- type Budget
- func (b Budget) Blockers(kind string) []string
- func (b *Budget) Complete(id string, changed bool, cost float64) error
- func (b *Budget) Extend(runID, action string, add Allowances, now time.Time) error
- func (b *Budget) Reserve(id, runID, kind string) error
- func (b Budget) ReservedRevisions() int
- func (b *Budget) Tick(now time.Time)
- func (b *Budget) Warnings() []string
- type Candidate
- type Decision
- type Event
- type Extension
- type InvocationUsage
- type NodeRecord
- type Reservation
- type ReviewRecovery
- type Run
- type RuntimeIntegration
- type SessionContext
- type Store
- func (s *Store) AppendAttempt(nodeID string, att AttemptRecord) error
- func (s *Store) AppendDecision(d Decision) error
- func (s *Store) AppendEvent(e Event) error
- func (s *Store) ReadAllNodes() (map[string]NodeRecord, error)
- func (s *Store) ReadArtifact(artifact string) ([]byte, error)
- func (s *Store) ReadBudget() (Budget, error)
- func (s *Store) ReadDecisions() ([]Decision, error)
- func (s *Store) ReadEvents() ([]Event, error)
- func (s *Store) ReadNode(nodeID string) (NodeRecord, error)
- func (s *Store) ReadRun() (Run, error)
- func (s *Store) ReserveFanoutSlot(now time.Time, mutate func(run *Run, schedule *adaptive.Schedule) error) error
- func (s *Store) UpdateBudget(initial func() (Budget, error), update func(*Budget) error) error
- func (s *Store) UpdateFanout(now time.Time, mutate func(run *Run, schedule *adaptive.Schedule) error) error
- func (s *Store) UpdateIntegration(now time.Time, ...) error
- func (s *Store) UpdateState(st engine.State, now time.Time) (Run, error)
- func (s *Store) UpdateVerification(now time.Time, ...) error
- func (s *Store) WithRunLock(fn func() error) error
- func (s *Store) WriteArtifact(artifact string, data []byte) error
- func (s *Store) WriteNode(rec NodeRecord) error
- func (s *Store) WriteRun(r Run) error
- type TreeUsage
Constants ¶
const (
RunFileName = "run.json"
)
RunFileName and nodesDirName name the files within a run's directory.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Allowances ¶
type AttemptRecord ¶
type AttemptRecord struct {
Attempt int `json:"attempt"`
StartedAt string `json:"startedAt"`
Effect json.RawMessage `json:"effect,omitempty"`
ResolvedAt string `json:"resolvedAt,omitempty"`
Event json.RawMessage `json:"event,omitempty"`
}
AttemptRecord is one execution of a node's effect: the effect asked for and, once resolved, the event that resolved it. Encoded as opaque JSON so this package stays decoupled from engine's concrete Effect/Event types, the same reasoning as internal/sdlc/journal.
type Budget ¶
type Budget struct {
Version int `json:"version"`
Original Allowances `json:"original"`
Limits Allowances `json:"limits"`
Usage Allowances `json:"usage"`
Reservations map[string]Reservation `json:"reservations"`
Activities map[string]Activity `json:"activities"`
Extensions []Extension `json:"extensions,omitempty"`
Warned map[string]bool `json:"warned,omitempty"`
Checkpoint time.Time `json:"checkpoint"`
TimeEstimated bool `json:"timeEstimated,omitempty"`
InvocationSeconds float64 `json:"invocationSeconds"`
WorkflowSteps map[string]int `json:"workflowSteps,omitempty"`
}
Budget is the single admission ledger for an entire run tree. It lives in budget.json under the root, separately from run.json so a stale child/driver snapshot cannot overwrite a reservation or an operator's grant.
func (Budget) Blockers ¶
Blockers returns all allowances which would block the requested admission. Completed admitted work does not consult these limits.
func (Budget) ReservedRevisions ¶
type Decision ¶
type Decision struct {
At string `json:"at"`
RunID string `json:"runId"`
Kind string `json:"kind"`
Stage string `json:"stage,omitempty"`
Invocation string `json:"invocation,omitempty"`
Runtime string `json:"runtime,omitempty"`
Trigger string `json:"trigger,omitempty"`
Candidates []Candidate `json:"candidates,omitempty"`
Rubrics map[string]string `json:"rubrics,omitempty"`
Choice string `json:"choice,omitempty"`
Confidence *float64 `json:"confidence,omitempty"`
Outcome string `json:"outcome,omitempty"`
Next string `json:"next,omitempty"`
Detail string `json:"detail,omitempty"`
}
Decision is recorded evidence about a routing or lifecycle choice. Detail is display text, never a claim to contain Jev's private reasoning.
type Event ¶
type Event struct {
At string `json:"at"`
RunID string `json:"runId"`
Stage string `json:"stage"`
Outcome string `json:"outcome,omitempty"`
Agent string `json:"agent,omitempty"`
Runtime string `json:"runtime,omitempty"`
Invocation string `json:"invocation,omitempty"`
Reason string `json:"reason,omitempty"`
Workflow string `json:"workflow,omitempty"`
Assignments int `json:"assignments"`
Revisions int `json:"revisions"`
ChildRuns int `json:"childRuns"`
TransitionStage string `json:"transitionStage,omitempty"`
TransitionKind string `json:"transitionKind,omitempty"`
TransitionAnswer string `json:"transitionAnswer,omitempty"`
TransitionNext string `json:"transitionNext,omitempty"`
ChildRunID string `json:"childRunId,omitempty"`
}
type Extension ¶
type Extension struct {
At time.Time `json:"at"`
RunID string `json:"runId"`
Action string `json:"action"`
Before Allowances `json:"before"`
After Allowances `json:"after"`
}
type InvocationUsage ¶
type InvocationUsage struct {
Invocation string `json:"invocation"`
Agent string `json:"agent"`
Runtime string `json:"runtime"`
Model string `json:"model,omitempty"`
Role string `json:"role"`
SessionID string `json:"sessionId,omitempty"`
InputTokens *int64 `json:"inputTokens"`
OutputTokens *int64 `json:"outputTokens"`
ToolCalls *int64 `json:"toolCalls,omitempty"`
CacheReadTokens *int64 `json:"cacheReadTokens,omitempty"`
CacheCreationTokens *int64 `json:"cacheCreationTokens,omitempty"`
CostUSD *float64 `json:"costUsd,omitempty"`
// ElapsedMS is the invocation's wall-clock time; nil on records written
// before it was tracked.
ElapsedMS *int64 `json:"elapsedMs,omitempty"`
UsageProvenance string `json:"usageProvenance,omitempty"`
StablePrefixBytes int `json:"stablePrefixBytes,omitempty"`
StablePrefixFingerprint string `json:"stablePrefixFingerprint,omitempty"`
}
InvocationUsage uses pointers so an absent count remains unknown. Cache fields and provenance are omitempty so older run.json ledgers that never recorded them stay readable and round-trip without inventing zeros.
type NodeRecord ¶
type NodeRecord struct {
NodeID string `json:"nodeId"`
Attempts []AttemptRecord `json:"attempts,omitempty"`
}
NodeRecord is one node's execution history: nodes/<id>.json.
type Reservation ¶
type ReviewRecovery ¶
type ReviewRecovery struct {
ToolPolicyFingerprint string `json:"toolPolicyFingerprint,omitempty"`
RuntimeArgsFingerprint string `json:"runtimeArgsFingerprint,omitempty"`
Invocation string `json:"invocation"`
Agent string `json:"agent"`
Binding string `json:"binding"`
Runtime string `json:"runtime"`
Model string `json:"model,omitempty"`
Revision string `json:"revision"`
Outcome string `json:"outcome"`
Content string `json:"content,omitempty"`
Reason string `json:"reason,omitempty"`
SessionID string `json:"sessionId,omitempty"`
InputTokens *int64 `json:"inputTokens,omitempty"`
OutputTokens *int64 `json:"outputTokens,omitempty"`
ToolCalls *int64 `json:"toolCalls,omitempty"`
CacheReadTokens *int64 `json:"cacheReadTokens,omitempty"`
CacheCreationTokens *int64 `json:"cacheCreationTokens,omitempty"`
CostUSD *float64 `json:"costUsd,omitempty"`
UsageProvenance string `json:"usageProvenance,omitempty"`
Paths []string `json:"paths,omitempty"`
Truncated bool `json:"truncated,omitempty"`
Applied bool `json:"applied,omitempty"`
}
ReviewRecovery is durable evidence for a parsed review that was interrupted by workspace drift or a crash before its result was applied.
type Run ¶
type Run struct {
BudgetDefaults *Allowances `json:"budgetDefaults,omitempty"`
InvocationSeconds float64 `json:"invocationSeconds,omitempty"`
RunID string `json:"runId"`
SessionStrategy string `json:"sessionStrategy,omitempty"`
RuntimeIntegration *RuntimeIntegration `json:"runtimeIntegration,omitempty"`
RequirePlanApproval bool `json:"requirePlanApproval,omitempty"`
ApprovedPlanRevision string `json:"approvedPlanRevision,omitempty"`
ApprovedChecksRevision string `json:"approvedChecksRevision,omitempty"`
ApprovedSubtasksRevision string `json:"approvedSubtasksRevision,omitempty"`
// AuthorizedChecksRevision is the exact checks.json digest the operator
// authorized for this run. --auto alone never sets it.
AuthorizedChecksRevision string `json:"authorizedChecksRevision,omitempty"`
PlanFeedback string `json:"planFeedback,omitempty"`
// OperatorGuidance is free text the human supplied while the run was
// paused. It is added to the next agent prompt and cleared once an
// invocation completes with a real outcome.
OperatorGuidance string `json:"operatorGuidance,omitempty"`
Sessions map[string]string `json:"sessions,omitempty"`
// SessionContexts records content-free binding metadata so chooseSession can
// refuse silent resume when project/workdir or plan/diff revision drifts.
// Omitted on older run.json files; missing entries keep prior resume behavior
// until the next successful invocation writes a context.
SessionContexts map[string]SessionContext `json:"sessionContexts,omitempty"`
Usage []InvocationUsage `json:"usage,omitempty"`
ReviewRecovery *ReviewRecovery `json:"reviewRecovery,omitempty"`
WorkDir string `json:"workDir,omitempty"`
AllowRead []string `json:"allowRead,omitempty"`
TreeUsage *TreeUsage `json:"treeUsage,omitempty"`
// Fanout is the durable supervisor schedule for an approved subtask graph.
Fanout *adaptive.Schedule `json:"fanout,omitempty"`
// Integration is the supervisor-owned merge of fan-out subtask patches.
// Stored with the parent run so users can inspect or delete it together.
Integration *adaptive.IntegrationRecord `json:"integration,omitempty"`
// Verification is the supervisor-owned check pass for the current candidate.
// Receipts are owner-only under the run; diagnostic logs are separately
// prunable via `sdlc prune --logs-only`.
Verification *adaptive.VerificationRecord `json:"verification,omitempty"`
DelegateBuiltins bool `json:"delegateBuiltins,omitempty"`
AutoDecisionDone bool `json:"autoDecisionDone,omitempty"`
AutoDecisionReason string `json:"autoDecisionReason,omitempty"`
AutoChildRunID string `json:"autoChildRunId,omitempty"`
AutoTarget string `json:"autoTarget,omitempty"`
ParentRunID string `json:"parentRunId,omitempty"`
Depth int `json:"depth,omitempty"`
Workflow string `json:"workflow"`
SelectionReason string `json:"selectionReason,omitempty"`
GraphSHA256 string `json:"graphSha256"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
// Task is the run's task statement, stored intact (run.json is written
// at 0600, like every ledger file); it is redacted before it ever
// reaches Jev, but kept whole here for a human or `sdlc status` to read.
Task string `json:"task,omitempty"`
State engine.State `json:"state"`
Adaptive *adaptive.State `json:"adaptive,omitempty"`
StageFlow *stageflow.State `json:"stageFlow,omitempty"`
}
type RuntimeIntegration ¶
type RuntimeIntegration struct {
Hooks bool `json:"hooks"`
Compaction bool `json:"compaction"`
MCP bool `json:"mcp,omitempty"`
}
Run is one run's authoritative record: run.json.
type SessionContext ¶
type SessionContext struct {
WorkDir string `json:"workDir,omitempty"`
PlanRevision string `json:"planRevision,omitempty"`
DiffRevision string `json:"diffRevision,omitempty"`
}
SessionContext is content-free metadata for a stored runtime session ID.
type Store ¶
type Store struct {
Dir string
// contains filtered or unexported fields
}
Store is a run's ledger directory: <root>/<runId>/.
func Open ¶
Open returns the Store for runID under root. It does not touch disk. A runID containing a path separator, or equal to "." or "..", makes every subsequent read/write on the returned Store return an error instead of resolving outside root.
func (*Store) AppendAttempt ¶
func (s *Store) AppendAttempt(nodeID string, att AttemptRecord) error
AppendAttempt loads a node's record, appends att, and writes it back. Callers needing this under concurrent writers should serialize their own updates (e.g. one goroutine driving a given run); WriteNode's atomic rename only guarantees a reader never sees a torn file, not read-modify- write isolation across the two calls.
func (*Store) AppendDecision ¶
AppendDecision serializes complete JSON lines. Callers supply already redacted text; this layer also bounds every free text field.
func (*Store) AppendEvent ¶
func (*Store) ReadAllNodes ¶
func (s *Store) ReadAllNodes() (map[string]NodeRecord, error)
ReadAllNodes returns every node record in the run's ledger, sorted by node id. A run with no nodes directory yet returns none, not an error.
func (*Store) ReadArtifact ¶
ReadArtifact reads a previously stored artifact.
func (*Store) ReadBudget ¶
func (*Store) ReadDecisions ¶
func (*Store) ReadEvents ¶
func (*Store) ReadNode ¶
func (s *Store) ReadNode(nodeID string) (NodeRecord, error)
ReadNode reads nodes/<nodeID>.json. A missing record returns a zero-value NodeRecord (no attempts yet), not an error: a node the run hasn't reached yet simply has none.
func (*Store) ReserveFanoutSlot ¶
func (s *Store) ReserveFanoutSlot(now time.Time, mutate func(run *Run, schedule *adaptive.Schedule) error) error
ReserveFanoutSlot serializes schedule mutation under the run lock so simultaneous drive/host requests cannot oversubscribe or double-launch.
func (*Store) UpdateBudget ¶
UpdateBudget holds the root's dedicated budget lock. Lock order is run lock, then budget lock; callbacks must never acquire a run lock. Atomic rename commits reservations, accounting and extension history together.
func (*Store) UpdateFanout ¶
func (s *Store) UpdateFanout(now time.Time, mutate func(run *Run, schedule *adaptive.Schedule) error) error
UpdateFanout serializes an arbitrary schedule update under the run lock.
func (*Store) UpdateIntegration ¶
func (s *Store) UpdateIntegration(now time.Time, mutate func(run *Run, integration *adaptive.IntegrationRecord) error) error
UpdateIntegration serializes supervisor integration state under the run lock.
func (*Store) UpdateState ¶
UpdateState loads run.json, applies st, stamps UpdatedAt as now, and writes the result back — the one call a caller needs after every engine.Apply.
func (*Store) UpdateVerification ¶
func (s *Store) UpdateVerification(now time.Time, mutate func(run *Run, verification *adaptive.VerificationRecord) error) error
UpdateVerification serializes supervisor verification receipts under the run lock.
func (*Store) WithRunLock ¶
WithRunLock serializes read-modify-write operations on one run across processes. Individual file writes remain atomic under their own locks.
func (*Store) WriteArtifact ¶
WriteArtifact atomically stores a seeded or produced artifact's content under the run's artifacts directory.
func (*Store) WriteNode ¶
func (s *Store) WriteNode(rec NodeRecord) error
WriteNode atomically writes rec to nodes/<rec.NodeID>.json.
type TreeUsage ¶
type TreeUsage struct {
Assignments int `json:"assignments"`
Revisions int `json:"revisions"`
ChildRuns int `json:"childRuns"`
StageSteps int `json:"stageSteps"`
EstimatedCostUSD float64 `json:"estimatedCostUsd,omitempty"`
}
TreeUsage is charged at the root as work is reserved or completed.