schema

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: May 31, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package schema loads and validates .agent-memory/meta/schema.yaml. The schema declares the categories of files the server knows about (durable decisions, modules, archive, local current/sessions, server-managed index) and the per-category policy that drives M3 approval routing and validation.

The manifest (internal/config) imports this package for ApprovalMode and references categories by name in its updates.approval block.

See docs/patterns/configuration-loading.md and design doc v0.4.1 §25.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func WriteDefault

func WriteDefault(schemaPath string) error

WriteDefault writes the recommended schema to schemaPath. Used by `agent-memory init` (T1.10).

func WriteSchema

func WriteSchema(schemaPath string, s *Schema) error

WriteSchema serialises s to schemaPath atomically.

Types

type ApprovalMode

type ApprovalMode string

ApprovalMode is the approval policy assigned to a category or (in the manifest) overridden per-operation.

const (
	// ApprovalApply means writes are applied immediately after validation.
	ApprovalApply ApprovalMode = "apply"
	// ApprovalStage means writes are staged for human review via the
	// review/apply CLI commands.
	ApprovalStage ApprovalMode = "stage"
	// ApprovalServerOnly means agents may not write to this category at all;
	// the server maintains it (e.g., index.md).
	ApprovalServerOnly ApprovalMode = "server_only"
)

func (ApprovalMode) IsValid

func (m ApprovalMode) IsValid() bool

IsValid reports whether m is one of the recognised ApprovalMode values.

type Category

type Category struct {
	// Name is populated from the map key by populateCategoryNames() after
	// load. It is NOT read from YAML directly.
	Name string `yaml:"-"`

	// Exactly one of File or FileGlob identifies the category's files.
	File     string `yaml:"file,omitempty"`
	FileGlob string `yaml:"file_glob,omitempty"`

	// SectionIDRequired turns on the AssignMissingIDs pass during init and
	// rebuild-index for files in this category.
	SectionIDRequired bool `yaml:"section_id_required,omitempty"`

	// SectionSchema is the per-section structural schema (required fields,
	// patterns, enums). M1 stores it verbatim; M3 will validate against it.
	SectionSchema *SectionSchema `yaml:"section_schema,omitempty"`

	// Approval is the default approval policy for writes to this category.
	// The manifest may override per-operation in updates.approval.
	Approval ApprovalMode `yaml:"approval,omitempty"`

	// ServerManaged means the file is written only by the server (e.g.,
	// index.md). AgentWritable must be false in this case.
	ServerManaged bool `yaml:"server_managed,omitempty"`
	AgentWritable bool `yaml:"agent_writable,omitempty"`

	// WriteOnce means files in this category may not be modified after
	// creation (e.g., archive/*.md).
	WriteOnce bool `yaml:"write_once,omitempty"`

	// GitTracked indicates whether agent-memory init's .gitignore should
	// keep this file out of git. false here corresponds to entries in
	// .agent-memory/.gitignore (current/, sessions/, etc.).
	GitTracked bool `yaml:"git_tracked"`

	Provenance Provenance `yaml:"provenance,omitempty"`
}

Category declares the rules for a class of memory files.

type FieldSpec

type FieldSpec struct {
	Name    string   `yaml:"name"`
	Pattern string   `yaml:"pattern,omitempty"`
	Enum    []string `yaml:"enum,omitempty"`
}

FieldSpec describes one labelled field inside a section's body (e.g., "Date: 2026-05-26").

type Provenance

type Provenance struct {
	Required               bool     `yaml:"required,omitempty"`
	RequiredForNewSections bool     `yaml:"required_for_new_sections,omitempty"`
	AllowedSourceTypes     []string `yaml:"allowed_source_types,omitempty"`
	ForbiddenSourceTypes   []string `yaml:"forbidden_source_types,omitempty"`
}

Provenance is the source-attribution policy for a category.

type Schema

type Schema struct {
	Version    string              `yaml:"version"`
	Categories map[string]Category `yaml:"categories"`
}

Schema is the top-level deserialisation target for schema.yaml.

func DefaultSchema

func DefaultSchema() *Schema

DefaultSchema returns the recommended schema from design doc v0.4.1 §25.1. The returned Schema has Category.Name populated for every entry — callers who consume *Schema directly (without going through LoadSchema) get a self-consistent struct.

func LoadSchema

func LoadSchema(schemaPath string) (*Schema, error)

LoadSchema reads schema.yaml from schemaPath. The YAML is decoded into a fresh Schema, then merged INTO DefaultSchema field-by-field so partial overrides work intuitively:

  • YAML mentioning one field in one category overrides only that field; the rest of the category and all other categories keep their defaults.
  • User-defined categories not in defaults are accepted verbatim.

Merge semantics for individual Category fields:

  • String / pointer / slice fields: empty/nil/[] in YAML means "not set"; the default is preserved.
  • Bool fields: true overrides; false is treated as "not set" because Go's zero value for bool is indistinguishable from an explicit `field: false` in YAML. To flip a default-true bool to false, write the full category structure (documented limitation).

Naive yaml.v3 merge does NOT work here because it preserves only map KEYS (categories not mentioned in YAML survive), not field-level merge INTO existing map VALUES. The custom merge below handles both layers.

func (*Schema) CategoryForPath

func (s *Schema) CategoryForPath(rel string) (Category, bool)

CategoryForPath returns the category whose File or FileGlob matches rel. rel must use forward slashes (the canonical form used in design and manifest examples).

Lookup order:

  1. Exact File match.
  2. FileGlob via path.Match — uses '/' as the separator on every OS, so '*' in the glob never spans directory boundaries. (path/filepath would use '\' on Windows, letting '*' eat slashes, which is wrong for our convention.)

The returned Category has Name populated from the map key, even if the Schema didn't go through populateCategoryNames() upstream. Callers can rely on Name being non-empty whenever ok == true.

Returns (Category{}, false) if no category matches.

func (*Schema) Validate

func (s *Schema) Validate() error

Validate checks basic invariants on s:

  • Version is non-empty.
  • Every category has either File or FileGlob (never both empty).
  • Every Approval value (if non-empty) is a recognised ApprovalMode.
  • ServerManaged categories are not AgentWritable.

type SectionSchema

type SectionSchema struct {
	RequiredTopLevelHeading  bool        `yaml:"required_top_level_heading,omitempty"`
	PerSectionRequiredFields []FieldSpec `yaml:"per_section_required_fields,omitempty"`
	PerSectionOptionalFields []FieldSpec `yaml:"per_section_optional_fields,omitempty"`
}

SectionSchema describes the required structure of an individual section within a category file. The validator that consumes these fields lands in M3; M1 only stores and round-trips them.

type SectionViolation

type SectionViolation struct {
	Field   string // the FieldSpec.Name; empty for category-level violations
	Message string // human-readable
}

SectionViolation is one rule-broken finding from ValidateSection.

func ValidateSection

func ValidateSection(cat Category, sectionBody []byte) []SectionViolation

ValidateSection checks one section's body against the category's SectionSchema (required fields, patterns, enums). Returns a slice of violations; empty slice means the body conforms (or the category declares no SectionSchema, in which case anything goes).

Field parsing is intentionally simple: single-line `Name: Value` pairs. Multi-line values, indented continuations, and structured sub-fields are not supported in M3. The validator runs on the section body bytes (everything after the heading line and optional @id anchor) — the caller is responsible for slicing that out.

Examples of what gets caught for the decisions category:

## Use Postgres
<!-- @id: use-postgres -->

Status: active            ← matched against Status enum
Confidence: confirmed     ← matched against Confidence enum
(no Date field)           ← reported as missing required field

func (SectionViolation) String

func (v SectionViolation) String() string

Jump to

Keyboard shortcuts

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