Documentation
¶
Overview ¶
Package memory is the durable, per-repository handoff journal: discrete, categorized records (one markdown file per entry, YAML frontmatter carrying the structured fields) plus a legacy cursor snapshot. It is the one place that owns where journal entries live and how a record is serialized. Two consumers read and write through it: the MCP tool (internal/handler/mcp) and the console RPC (internal/handler/memory).
There is NO knowledge-graph shard over these records. This comment used to name an "@memory shard" among the consumers and it does not exist - knowledge.Inputs has a Notes field and no Memory one, and AssembleShards builds nothing for it. So a record is invisible to `magus query`, carries no edges to what it points at, and cannot be drift-checked: nothing anchors it. See RefKind on what that costs.
The store lives in the user's XDG state directory, NOT the repo (a developer's working memory does not belong in a shared checkout) and NOT the cache (evictable). It is keyed by repository identity, so every worktree of one repo shares one memory.
Index ¶
- Variables
- func Delete(root, name string, allowMissing bool) error
- func Dir(root string) (string, error)
- func Inspect(root string) ([]Record, []Issue, error)
- func ReadCursor(root string) (string, error)
- func Validate(r Record) error
- type Issue
- type Record
- type RecordType
- type Ref
- type RefKind
- type RefResolver
- type Verification
Constants ¶
This section is empty.
Variables ¶
var ErrUnknownType = errors.New("memory: unknown record type")
ErrUnknownType marks a record whose Type this binary does not know. Writing one is an error; reading one is not. The journal is durable user data shared by binaries of many ages, so a type a newer magus introduced has to degrade to a skipped entry with a warning, leaving the rest of the journal readable by the older binary that met it.
Functions ¶
func Delete ¶
Delete removes a record. allowMissing decides whether deleting an absent record is a no-op (AIP-135 idempotent delete) or an error.
func Dir ¶
Dir resolves the per-repository memory directory: <XDG state>/magus/memory/<repo-basename>-<hash12>. The hash keys on repository identity, not the checkout path, so every worktree of a repo shares one memory.
func Inspect ¶ added in v0.4.0
Inspect returns readable entries and every detected issue. It is for frontends that can present warnings alongside records; callers that only need safe records use List.
func ReadCursor ¶
ReadCursor returns the cursor snapshot ("where did I leave off"), or "" if unwritten.
func Validate ¶
Validate enforces the record schema on the way IN (the rules the whole feature rests on): a known type, at least one ref, a known kind on every ref, and prose only where it is allowed. Rejecting a bad record at the door keeps the store, the graph, and the console from ever holding a shape the model does not expect.
It also runs on the way OUT, where its verdict is advisory: readRecordFile reports a failure as an issue against that one file and scan skips it.
Types ¶
type Issue ¶ added in v0.4.0
type Issue struct {
Severity string `json:"severity"`
Code string `json:"code"`
Path string `json:"path"`
Record string `json:"record,omitempty"`
Message string `json:"message"`
Hint string `json:"hint"`
}
Issue is one actionable problem found by Verify. Severity says whether a human has repair work to do, and no severity withholds a record: every problem here is scoped to one entry, so the readable entries are always returned beside it.
type Record ¶
type Record struct {
Name string `json:"name" yaml:"name"`
Type RecordType `json:"type" yaml:"type"`
Status string `json:"status,omitempty" yaml:"status,omitempty"`
Refs []Ref `json:"refs" yaml:"refs"`
References []string `json:"references,omitempty" yaml:"references,omitempty"`
Created int64 `json:"created" yaml:"created,omitempty"`
Updated int64 `json:"updated" yaml:"updated,omitempty"`
Excerpt string `json:"excerpt,omitempty" yaml:"excerpt,omitempty"`
Body string `json:"body,omitempty" yaml:"-"`
}
Record is one persisted memory. The payload is one or more typed Refs; Body is a prose caption present only for decision/plan/elimination records (empty for pointer). Created and Updated are unix seconds, stamped by the store (output-only to callers).
Excerpt belongs to an elimination alone: the captured evidence that falsified the hypothesis, copied in so the record outlives the ref beside it. It lives in the frontmatter because Body already owns everything after the closing delimiter, and the two round-trip separately.
func List ¶
List returns every readable record in name order, skipping the entries it cannot read. This journal is the surface a human uses to find and delete a bad entry, so a corrupt file that took the listing down would leave nowhere to make the repair from. Only a failure to read the store itself returns an error. Run Verify for what was skipped and why.
type RecordType ¶
type RecordType string
RecordType is the closed subject axis a record's Type may take. A named string type so the compiler carries the closed set the values below promise, not just Validate at runtime. pointer carries no prose; decision/plan carry a ref-anchored prose caption.
elimination is the falsified axis: a hypothesis an investigation ruled out. The other three report intent, a settled choice, and a location, so overloading one would make a listing misreport the entry.
const ( TypePointer RecordType = "pointer" TypeDecision RecordType = "decision" TypePlan RecordType = "plan" TypeElimination RecordType = "elimination" )
type Ref ¶
type Ref struct {
Kind RefKind `json:"kind" yaml:"kind"`
Target string `json:"target" yaml:"target"`
}
Ref is one typed pointer a record carries: Kind is the closed ref-kind (query/node/output/command/doc); Target is the payload (a node ID or path, an output ref token, or a raw query/command string).
type RefKind ¶
type RefKind string
RefKind is the closed set a Ref.Kind may take. node/doc/output name a magus-domain node; query/command are re-runnable strings.
All five are resolvable IN PRINCIPLE, which is not the same as resolved. This package knows none of the stores behind them, so Verify takes a RefResolver from its caller and reports a decayed ref only when it gets one. The "deferred Phase 2 shard" an earlier version of this comment pointed at was never built, so the graph side of the gap is still open.
An output ref is the shortest-lived of the five. Output blobs live under the checkout that produced them while this store is keyed by repository, so a ref minted in a worktree that has since been removed resolves from nowhere. That asymmetry is why an elimination copies its evidence into Excerpt.
A node ref's Target is additionally a notes-anchor-shaped string ("symbol:...", "file:...", "project:...", "target:..."), parsed by notes.ParseAnchor when `magus notes promote` turns a record into a note. That coupling is real and undeclared: this package names no anchor kinds, so nothing stops a Target that the notes vocabulary cannot read, and promotion silently skips the ones it cannot parse.
type RefResolver ¶ added in v0.4.0
RefResolver reports whether one external evidence ref can still be reopened, returning nil when it can. A kind the caller does not check resolves trivially.
The caller supplies it because the stores behind a ref are not this package's to know: the cache answers an output ref, the graph a node. Threading either store in here would make the record serializer depend on the engine to read its own files.
type Verification ¶ added in v0.4.0
Verification is the deterministic result of checking a handoff journal.
func Verify ¶ added in v0.4.0
func Verify(root string, resolve RefResolver) (Verification, error)
Verify scans every entry without hiding malformed files or broken journal links, then asks resolve whether each record's evidence can still be reopened. A missing store is valid and reports zero entries.
A decayed ref is a warning. The entry keeps whatever it copied inline, so it degrades and stays readable.