cairn

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package cairn is the mechanical half of nova-cairn, the optional checkpoint tool from nova-tools #248.

It opens session records, appends the friend's exact words with a real clock stamp, stable identifiers and source pointers, and builds a bounded index and coverage ledger over them — and nothing else. There is no seal, no consume, no delete, no grading, no consolidation and no liveness inference here: those are separate explicit choices the caller never gets by accident. The store is plain files under a caller-named directory, so a note is durable (fsync) before success is reported, independently of Redis and of any remote: local persistence is reported separately from remote publication, and neither implies replicated durability.

Layout under the store directory:

sessions/<session>.md        the readable record; header by convention only
entries/<session>/<id>.json  one file per entry, the source of truth
log.jsonl                    append-only event log feeding the ledger

A store that keeps ONE MARKDOWN FILE PER SESSION directly under it -- <session>.md, the shape a friend appending by hand already has -- is read as it stands. `open` on such a record is a no-op and `append` lands a dated `## <stamp> — <entry>` section at the end of the file, with no entries/ directory, no log and no index appearing beside it. The tool adapts to the store; the store is never converted to suit the tool.

Each entry file is written atomically via internal/atomicfile (exclusive temporary file beside target, explicit mode, fsync to media, atomic rename), so a retry after an interrupted append finishes the pointer without duplicating the entry and without touching other writers' files. Stale temporary files are never indexed.

Index

Constants

View Source
const (
	PublishNever     = "never"
	PublishManual    = "manual"
	PublishDeferred  = "deferred"
	PublishImmediate = "immediate"
)

Publish policies name who publishes a checkpoint and when. This slice implements no transport: every append reports Published=false and the policy travels with the entry, so a later explicit act can carry it.

Variables

This section is empty.

Functions

func EntryText

func EntryText(store, session, id string) (string, error)

EntryText returns the friend's words byte-for-byte: the store never rewrites, grades or consolidates what was appended.

func Open

func Open(store, session, source string, now time.Time, publish string) error

Open starts (or re-starts, idempotently) one session record. Concurrent records coexist: opening a second session never touches the first.

func SessionSource

func SessionSource(store, session string) (string, error)

SessionSource reads back the --source the session was opened with, from the open record in log.jsonl. The session file's header is convention only and is never parsed, so the log is where the pointer is read from.

CORRUPT PROVENANCE NEVER READS AS NONE. An absent log is a store with no open records in it (a bench store, or one opened before the log carried a source) and answers "" with no error, as does an open record with no source key. A log that exists and cannot be read is an error, and so is a line that may be this session's open record and does not decode as one: a line naming the session and the open event that is not valid JSON, or whose fields are not strings. Answering "" for either would let an append file source=- over a pointer that open recorded.

Types

type AppendResult

type AppendResult struct {
	Persisted bool
	Published bool
	Policy    string
	// Source is the pointer the entry carries: its own --source, or the
	// session's when the append named none.
	Source    string
	Duplicate bool
}

AppendResult separates what this slice guarantees from what it does not: Persisted is true once the note is fsync-durable on this machine; Published names the remote, which this slice never touches.

func Append

func Append(store, session, id, text, source string, now time.Time, publish string) (AppendResult, error)

Append stores the friend's exact prose under a stable entry id with a real clock stamp and source pointers. A retry of the same request succeeds with Duplicate=true and no second entry; the same id with different prose is a conflict, never an overwrite. Offline use succeeds: the result carries persisted=true with published=false, because local durability never waited for the remote.

type ConflictError

type ConflictError struct{ Msg string }

ConflictError is a retry that must not silently win: the same entry id carrying different prose. It is the caller's failure (exit 1), not a crash.

func (*ConflictError) Error

func (e *ConflictError) Error() string

type IndexRow

type IndexRow struct {
	Session string
	ID      string
	Stamp   time.Time
	Source  string
	Bytes   int
}

IndexRow is one mechanical row of the section/entry index.

func Index

func Index(store, session string, max int) ([]IndexRow, int, error)

Index builds the bounded section/entry index mechanically from the stored entries: no narrative is recopied, work events are linked by session/entry pointers, and a stale *.tmp from an interrupted append is never a row. session "" indexes every record; max <= 0 lifts the ceiling and returns everything with no MORE standing for the rest.

type Ledger

type Ledger struct {
	Sessions int
	Entries  int
}

Ledger is the coverage count, derived from the store rather than remembered: how many session records exist and how many entries they hold.

func Coverage

func Coverage(store string) Ledger

Coverage derives the ledger from the store: session records and stored entries counted, never remembered, so the number cannot drift from the tree it reports on.

type NotFoundError

type NotFoundError struct{ Msg string }

NotFoundError is a use-before-open or a receipt for nothing stored.

func (*NotFoundError) Error

func (e *NotFoundError) Error() string

type ReceiptInfo

type ReceiptInfo struct {
	Session   string
	ID        string
	Stamp     time.Time
	Source    string
	Bytes     int
	Policy    string
	Persisted bool
	Published bool
}

ReceiptInfo is what receipt and index read back: the stamp, the pointer, the size, and the same persisted/published split the append reported.

func Receipt

func Receipt(store, session, id string) (ReceiptInfo, error)

Receipt names what was preserved for one entry: its stamp, pointers and size, with the persisted/published split repeated so a reader never has to infer the remote from the local.

Jump to

Keyboard shortcuts

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