rollup

package
v0.2.1 Latest Latest
Warning

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

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

Documentation

Overview

Package rollup turns episodic events into proposed vault notes.

The contract of the two-tier design lives here: raw events go in, notes the user has approved come out, and nothing is written to markdown that cannot be traced back to specific observed rows.

Index

Constants

View Source
const IdleGapS int64 = 15 * 60

IdleGapS is how long a silence has to run before the next activity counts as a new session rather than a continuation. Fifteen minutes is long enough to survive a coffee break and short enough to separate genuinely different stretches of work.

View Source
const ObservationsHeading = "## Observations"

ObservationsHeading is the only part of a note this system may append to.

View Source
const QueueSchema = `` /* 396-byte string literal not displayed */

Variables

This section is empty.

Functions

func Apply

func Apply(db *sql.DB, vaultDir string, p Proposal) error

Apply commits an accepted proposal to the vault.

func Enqueue

func Enqueue(db *sql.DB, p *Proposal) error

func InitQueue

func InitQueue(db *sql.DB) error

func NearMiss

func NearMiss(db *sql.DB, name, kind string) []string

NearMiss finds existing notes of the same kind with a similar name, for proposing a merge rather than creating a near-duplicate.

func PendingCount

func PendingCount(db *sql.DB) (int, error)

func ProposeRoutines

func ProposeRoutines(db *sql.DB, rt *router.Router, periodics []routine.Periodic, sequences []routine.Sequence) (int, error)

Propose turns mined patterns into queue entries.

Routines go through the same review queue as everything else — there is no privileged write path. A discovered pattern is still an inference about the user, and inferences get approved.

func SetStatus

func SetStatus(db *sql.DB, id int64, s Status) error

SetStatus records the decision. Rejections are kept, not deleted: "you proposed this and I said no" is the signal for tuning the auto-accept threshold later.

Types

type Category

type Category string

Category of work in a session.

const (
	Work     Category = "work"
	Comms    Category = "comms"
	Research Category = "research"
	Idle     Category = "idle"
)

type Classification

type Classification struct {
	Category Category `json:"category"`
	Summary  string   `json:"summary"`
}

type Entity

type Entity struct {
	Name string `json:"name"`
	Type string `json:"type"`
}

type Extractor

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

func NewExtractor

func NewExtractor(rt *router.Router) *Extractor

func (*Extractor) ClassifySession

func (x *Extractor) ClassifySession(s Session) (Classification, string, error)

func (*Extractor) ExtractEntities

func (x *Extractor) ExtractEntities(s Session) ([]Entity, string, error)

func (*Extractor) WriteDaily

func (x *Extractor) WriteDaily(date string, sessions []Session, classes []Classification) (string, string, error)

WriteDaily produces the day's prose from the session table.

Built from session digests, never from previously written summaries. Weekly notes built from daily notes drift into mush within a month, so every rollup re-derives from the episodic source.

type Kind

type Kind string

Kind of change a proposal would make to the vault.

const (
	NewNote Kind = "new_note"
	Append  Kind = "append"
	NewEdge Kind = "new_edge"
	Merge   Kind = "merge"
)

type Match

type Match struct {
	Slug string
	// Certain is true for an exact slug or alias hit. Uncertain matches become
	// merge proposals for the user to settle rather than silent decisions.
	Certain bool
}

func Resolve

func Resolve(db *sql.DB, name, kind string) (Match, bool)

Resolve finds the existing note an entity refers to, if any.

Deliberately only does the two cheap, safe steps: exact slug and declared alias. Embedding-similarity matching is available but is routed through a merge proposal instead of applied here — a wrong automatic merge silently destroys information, while a wrong proposal costs one keystroke.

type Payload

type Payload struct {
	Title string `json:"title,omitempty"`
	Type  string `json:"type,omitempty"`
	Body  string `json:"body,omitempty"`
	// Edge fields.
	Pred string `json:"pred,omitempty"`
	Obj  string `json:"obj,omitempty"`
	// Merge field: the slug to fold Target into.
	Into string `json:"into,omitempty"`
}

Payload carries the kind-specific detail. One struct rather than four keeps the queue table and the review UI simple; unused fields stay empty.

type Proposal

type Proposal struct {
	ID       int64
	Kind     Kind
	Target   string // note slug
	Payload  Payload
	Conf     float64
	Evidence []int64 // event ids
	Model    string
	Created  int64
	Status   Status
}

Proposal is a change the system would like to make, and never makes on its own until the user raises the auto-accept threshold.

Evidence is mandatory and enforced at write time. A proposal that cannot be traced back to observed events is a hallucination with good manners, and it must be impossible to accept one.

func Braindump

func Braindump(db *sql.DB, rt *router.Router, text string) (*Proposal, string, error)

Braindump classifies raw text and queues a proposal for it. Returns the proposal so the caller can report what it decided.

Tasks are handled by the caller (they become open loops, not notes); this returns the classification for everything, and a note proposal for the rest.

func List

func List(db *sql.DB, status Status) ([]Proposal, error)

func (Proposal) Summary

func (p Proposal) Summary() string

Summary is the one-line form shown in the review queue.

func (*Proposal) Validate

func (p *Proposal) Validate() error

Validate enforces the invariants that make the queue trustworthy. Called on every insert rather than trusted from the model.

Every field here is model output, written by a small local model reading captured text — web pages, other people's messages, whatever was on the clipboard. So the target is checked for staying inside the vault (joining it on unchecked made accepting a proposal a way to write a file anywhere the user can write) and the one-line fields are checked for the newlines that would let a note's frontmatter be forged from its own title.

type Result

type Result struct {
	Sessions  int
	DailyPath string
	Proposals int
	Skipped   int
	Loops     int
}

Run rolls a single day up into a daily note and a set of proposals.

The order matters: entities are resolved against the existing vault *before* any note is proposed, so a near-duplicate becomes a merge proposal rather than a second note.

func Day

func Day(db *sql.DB, vaultDir string, rt *router.Router, date time.Time, dryRun bool) (Result, error)

type Session

type Session struct {
	Start, End int64
	Events     []event.Event
	// EventIDs is the evidence set. Every proposal derived from this session
	// cites these.
	EventIDs []int64
}

Session is a contiguous stretch of activity. This is the unit the model reasons about — never individual events, which carry no context, and never a whole day, which exceeds what a small local model handles well.

func Sessionise

func Sessionise(events []event.Event) []Session

Sessionise groups events into sessions separated by idle gaps.

Incidental focus flickers are dropped here rather than downstream: they add noise to the model's context without adding information, and a small model spends its limited attention on whatever it is shown.

func (Session) Apps

func (s Session) Apps() []string

Apps returns time spent per app, descending.

func (Session) Digest

func (s Session) Digest(maxItems int) string

Digest renders a session for a model: compact, deduplicated, and capped.

Feeding 400 near-identical rows to an 8B model wastes context and degrades the output. What matters is which apps, which pages, which commits — not the exact ordering of every tab switch.

func (Session) DigestForEntities

func (s Session) DigestForEntities(maxItems int) string

DigestForEntities is Digest minus the host list.

Showing a small model a list of visited domains reliably produces a list of domains back, regardless of how the prompt is worded. The entities actually worth a note come from window titles and commit subjects — text the user or their collaborators wrote — so that is all this carries.

func (Session) DurS

func (s Session) DurS() int64

type Status

type Status string
const (
	Pending  Status = "pending"
	Accepted Status = "accepted"
	Rejected Status = "rejected"
)

Jump to

Keyboard shortcuts

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