ledger

package
v0.45.0 Latest Latest
Warning

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

Go to latest
Published: Oct 28, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Overview

Package ledger is f10's local record of what runs did: one append-only events table in ~/.f10/f10.db, outside every repo, so stealth is unaffected. Hooks write what they observe, `f10 state` what the model claims, and artifacts (plans, findings) what they hold. Beside it, task_refs keeps each task's last-read title and url, overwritten in place.

A ledger write never fails its caller: an event that cannot reach the database goes to a spool file beside it, which the next successful open replays.

Index

Constants

View Source
const (
	BusyAsync = 3 * time.Second
	BusySync  = 200 * time.Millisecond
)

Busy timeouts: under the 5s hook timeout for async writers, so a contended write spools instead of the hook being killed; short for a synchronous hook, so a busy ledger never delays a skill's start.

View Source
const (
	Observed = "observed"
	Artifact = "artifact"
	Claimed  = "claimed"
)

Sources: what the hook saw, what an artifact holds, what the model said.

View Source
const (
	RunStarted     = "run_started"
	TaskCaptured   = "task_captured"
	StepEntered    = "step_entered"
	Committed      = "committed"
	Pushed         = "pushed"
	PROpened       = "pr_opened"
	TaskFinished   = "task_finished"
	TaskHalted     = "task_halted" // drive's, not a hook's: a task it stopped
	SessionStopped = "session_stopped"
	WaitStarted    = "wait_started"
	WaitEnded      = "wait_ended"
	TaskSized      = "task_sized" // finish's: the branch's diffstat before it lands

	PlanSaved         = "plan_saved"
	PlanSuperseded    = "plan_superseded"
	ReviewRoundOpened = "review_round_opened"
	FindingVerdict    = "finding_verdict"
	JudgeVerdict      = "judge_verdict"

	StateSet   = "state_set"
	StateTask  = "state_task"
	StateNote  = "state_note"
	StateAsk   = "state_ask"
	StateFinal = "state_final"
)

Kinds, by source.

View Source
const PathEnv = "F10_LEDGER"

PathEnv overrides the database's path; the spool and machine id live beside it, so a test pointing it at a temp dir touches nothing else.

Variables

This section is empty.

Functions

func LookupRefs added in v0.44.0

func LookupRefs(ctx context.Context, project string, ids []string) map[string]TaskRef

LookupRefs reads project's rows for ids without writing anything. A missing database, or one no run has migrated to hold refs yet, knows none.

func MachineID

func MachineID(dir string) string

MachineID is this machine's id, generated once into <dir>/machine-id. Created exclusively, so two first runs read back the same id. Empty when the directory cannot be written.

func NewPayload

func NewPayload(p Payload) r3.JSONColumn[Payload]

NewPayload wraps a payload for an Event.

func NormalizeRemote

func NormalizeRemote(url string) string

NormalizeRemote turns a git remote url into a project id, so one repo cloned over ssh and over https is one project: scheme, user and a trailing `.git` dropped, scp-style `host:path` read as `host/path`, the host lowercased. `git@github.com:a/b.git` and `https://github.com/a/b` are both `github.com/a/b`. A local path stays a cleaned path.

func Path

func Path() string

Path is the database file: F10_LEDGER, else ~/.f10/f10.db.

func Record

func Record(ctx context.Context, busy time.Duration, events ...Event)

Record opens the ledger at Path, appends events and closes it. An open that fails spools them; nothing is returned, since no caller may act on a ledger failure.

func RememberRefs added in v0.44.0

func RememberRefs(ctx context.Context, project string, refs ...TaskRef)

RememberRefs opens the ledger at Path, writes refs and closes it. Like Record it never fails its caller, but a ref that cannot be written is dropped rather than spooled: the next read of the task rebuilds it.

Types

type Event

type Event struct {
	ID           string     `db:"id,pk"                   json:"id"`
	Machine      string     `db:"machine,immutable"       json:"machine"`
	At           time.Time  `db:"at,immutable"            json:"at"`
	Project      string     `db:"project,immutable"       json:"project"`
	ProjectLocal bool       `db:"project_local,immutable" json:"project_local"`
	Checkout     string     `db:"checkout,immutable"      json:"checkout"`
	Branch       string     `db:"branch,immutable"        json:"branch"`
	Session      string     `db:"session,immutable"       json:"session"`
	Task         string     `db:"task,immutable"          json:"task"`
	Kind         string     `db:"kind,immutable"          json:"kind"`
	Source       string     `db:"source,immutable"        json:"source"`
	Payload      payloadCol `db:"payload,immutable"       json:"payload"`
}

Event is one row. Every column but the id is immutable, so r3 has no update path for it and an upsert of a known id does nothing: the table only grows.

func (Event) TableName

func (Event) TableName() string

type Filter

type Filter struct {
	Project string
	Task    string
	Session string
	Kind    string
}

Filter narrows List; an empty field matches every row.

type Ledger

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

A Ledger is an open database.

func Open

func Open(ctx context.Context, path string, busy time.Duration, replay bool) (*Ledger, error)

Open opens the database at path for writing: it creates the file and runs the migrations. With replay it also replays the spool, which a synchronous hook leaves to the next async writer: a backlog has no bound.

func OpenRead

func OpenRead(ctx context.Context, path string) (*Ledger, error)

OpenRead opens an existing database read-only: no migration, no replay, so reading never writes. A missing file is fs.ErrNotExist.

func (*Ledger) Append

func (l *Ledger) Append(ctx context.Context, events ...Event)

Append writes events, ids and machine filled in where empty. An event the database refuses goes to the spool with the ones after it.

func (*Ledger) Close

func (l *Ledger) Close() error

Close closes the database.

func (*Ledger) Count

func (l *Ledger) Count(ctx context.Context, f Filter) (int64, error)

Count is how many events f matches.

func (*Ledger) List

func (l *Ledger) List(ctx context.Context, f Filter) ([]Event, error)

List returns the events f matches, oldest first.

func (*Ledger) Refs added in v0.44.0

func (l *Ledger) Refs(ctx context.Context, project string, ids []string) (map[string]TaskRef, error)

Refs is the rows project holds for ids, by id; an id never read is absent.

func (*Ledger) Remember added in v0.44.0

func (l *Ledger) Remember(ctx context.Context, project string, refs ...TaskRef) error

Remember writes refs for project, each replacing the row for its id. A ref with no title is skipped: it would only hide a later lookup's miss. One with no url keeps the stored one, since some reads give a title alone.

type Observer

type Observer struct {
	StateDir string
	Busy     time.Duration
	// NoReplay leaves the spool to a later writer: a synchronous hook holds
	// the prompt while it runs.
	NoReplay bool
	// TaskID is the tracker task id a created issue's url names.
	TaskID func(ctx context.Context, cwd, url string) string
	// contains filtered or unexported fields
}

Observer records a hook's observations. It opens the ledger on the first one, so an event that yields nothing never touches the database; Close releases it.

func (*Observer) Close

func (ob *Observer) Close()

Close closes the ledger if an observation opened it.

func (*Observer) Observe

func (ob *Observer) Observe(ctx context.Context, o observe.Observation) error

type Payload

type Payload = map[string]any

Payload is an event's kind-specific detail, stored as JSON.

type Place

type Place struct {
	Project      string
	ProjectLocal bool // no remote: the main checkout's path stands in
	Checkout     string
	Branch       string
}

Place is where an event happened: the project, the checkout and its branch.

func Where

func Where(ctx context.Context, dir string) Place

Where reads the place a directory is in. Outside git, everything is empty but the project, which is the directory itself, flagged local.

type TaskRef added in v0.44.0

type TaskRef struct {
	Key     string    `db:"id,pk"             json:"-"`
	Project string    `db:"project,immutable" json:"project"`
	ID      string    `db:"task,immutable"    json:"id"`
	Title   string    `db:"title"             json:"title"`
	URL     string    `db:"url"               json:"url"`
	SeenAt  time.Time `db:"seen_at"           json:"seen_at"`
}

A TaskRef is a task's title and url as f10 last read them from the tracker. Unlike an event it is overwritten by the next read: a renamed task stays stale until f10 reads it again.

func (TaskRef) TableName added in v0.44.0

func (TaskRef) TableName() string

Jump to

Keyboard shortcuts

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