lesson

package
v0.38.0 Latest Latest
Warning

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

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

Documentation

Overview

Package lesson parses and scaffolds canonical directory Lessons at spec/lessons/<slug>/README.md and compatibility flat Lessons at spec/lessons/<slug>.md.

A Lesson records a gap in process — a missing check, gate, convention, or review step that let a defect ship unnoticed — and climbs a three-rung enforcement ladder (Recorded -> Stated -> Enforced) as the gap is closed by an increasingly binding mechanism. It is deliberately the flattest kind in the spec tree: no hierarchy, no source-Feature dependency, a single status line. Canonical READMEs keep the durable rule compact while immutable child occurrences hold manifestation history; compatibility flat files retain the historical four-section prose contract until explicit migration.

Package lesson — lifecycle transition orchestration for the Lesson kind.

This file hosts ChangeStatus, the kind-specific orchestrator invoked by `specscore lesson change-status`. It composes pkg/lifecycle/ primitives (state-machine validation, status-line rewrite, rollback) and adds the Lesson-specific structured `**Superseded By:**` successor reference — mirroring pkg/plan/transitions.go exactly, minus the execution band (a Lesson has none: every rung of the ladder is human-authored).

LINT INVOCATION lives in the cobra adapter (internal/cli/lesson.go), NOT here, to avoid an import cycle: pkg/lint imports pkg/lesson for the lesson-* lint rules, so pkg/lesson cannot depend back on pkg/lint. The adapter passes a PostMutationHook callback into ChangeStatus; this package only knows "run the post-mutation hook, and roll back if it fails".

Index

Constants

View Source
const FormatURL = "https://specscore.md/lesson-specification"

FormatURL is the canonical spec URL for the Lesson document type. It is carried verbatim in both the frontmatter `format:` field and the adherence-footer line, per the artifact-frontmatter-convention.

View Source
const OccurrenceStoreKeepFile = ".gitkeep"

OccurrenceStoreKeepFile preserves an otherwise empty canonical occurrence store in Git. It is not an occurrence and must remain empty.

Variables

View Source
var RequiredSections = []string{"Incident", "Process gap", "Check", "Enforcement"}

RequiredSections is the closed, ordered set of H2 section headings every Lesson body MUST declare. Lint checks presence ONLY — never content, length, or wording — so a section holding nothing but a `<!-- TODO -->` prompt is lint-clean. Process gap and Enforcement are the load-bearing pair: a Lesson describing an Incident but naming no gap in the process is the useless entry the Process-gap requirement exists to refuse, and a Lesson proposing no enforcement path is the other half of that same refusal.

Functions

func AddRelation added in v0.34.0

func AddRelation(lessonsDir, from, typ, to string) error

func AddRelationTransaction added in v0.34.0

func AddRelationTransaction(lessonsDir, from, typ, to string, hooks RelationTransactionHooks) (bool, error)

AddRelationTransaction returns false without invoking BeforeMutation when the exact reviewed relation is already present. This lets CLI adapters avoid creating a second event for a completed idempotent command while still reconciling a retained prepared event through ReconcileNoop.

func AddRelationWithPostMutation added in v0.34.0

func AddRelationWithPostMutation(lessonsDir, from, typ, to string, postMutation RelationPostMutationHook) error

AddRelationWithPostMutation holds both endpoint locks (lexical slug order), then the project relation lock, across artifact publication and bounded index/durability reconciliation.

func CompensatePublication added in v0.34.0

func CompensatePublication(removeAndSync func() error, cause error) error

CompensatePublication runs the caller's exact inverse mutation and its durability fence. It returns a typed outcome so a prepared event is aborted only after full compensation is proven.

func FinalizeFlatMigration added in v0.34.0

func FinalizeFlatMigration(opts FlatMigrationOptions, eventUUID string) error

FinalizeFlatMigration retires the durable transaction marker only after the caller has durably committed the prepared event. It independently verifies the canonical artifacts, manifest, exact index projection, and removal of the flat source. A crash before this call leaves a marker that makes the CLI replay the same deterministic event and narrow index upsert.

func FlatMigrationEventUUID added in v0.34.0

func FlatMigrationEventUUID(sourceSHA, slug string) string

func IsSingleFileLessonPath

func IsSingleFileLessonPath(lessonsDir, filePath string) bool

IsSingleFileLessonPath reports whether filePath looks like a single-file Lesson candidate location — directly under lessonsDir, with a `.md` extension, and not named README.md (the index file). It does NOT read the file; callers still must validate the title prefix via Parse().

func LegalChangeStatusTargetNames

func LegalChangeStatusTargetNames() []string

LegalChangeStatusTargetNames returns the canonical-titled names of the legal --to values (every To column in the KindLesson matrix). Filtering lifecycle.LegalStatuses — itself already alphabetically sorted — preserves that order, so no separate sort step is needed.

func LegalTransitionMatrix

func LegalTransitionMatrix() string

LegalTransitionMatrix returns a human-readable, ANSI-free rendering of the Lesson legal-transition matrix, suitable for cobra `Long` help text.

func PreflightLegacyApply added in v0.34.0

func PreflightLegacyApply(lessonsDir string, allowedClassifications []string, inv LegacyInventory, mapping LegacyMapping) error

PreflightLegacyApply validates the complete reviewed mapping, configured classification vocabulary, immutable source bytes, targets, and manifest collisions without writing. CLI adapters call it before preparing an event; ApplyLegacy calls the same function again immediately before publication.

func Recur

func Recur(path, note string) (int, error)

Recur records that a Lesson's gap manifested again: it increments the `**Recurred:** N` header count (inserting the field, defaulted to 0 then incremented to 1, when a pre-existing Lesson predates it) and appends a dated bullet — with the optional note — to a `## Recurrences` section (created immediately before the adherence footer when absent). It does NOT change `**Status:**`: a recurrence is a signal that a lesson needs to graduate, not a graduation itself — the decision to promote stays a deliberate `change-status` call. Returns the new recurrence count.

func RecurWithPostMutation added in v0.34.0

func RecurWithPostMutation(path, note string, postMutation func(int) error) (int, error)

RecurWithPostMutation holds the per-Lesson lifecycle lock across the body rewrite and its bounded reconciliation hook. The hook may acquire the shared Lesson-index lock; the total lock order is therefore always per-Lesson first, shared index second.

func RelationToken added in v0.34.0

func RelationToken(from, typ, to string) string

func RemoveOccurrence added in v0.34.0

func RemoveOccurrence(path string) error

RemoveOccurrence performs an explicit caller-requested deletion and fences its parent directory. It is never safe as automatic post-publication compensation: a path returned by AddOccurrence is not an ownership token, because another writer can replace that path before a later unlink.

func RequiredSectionsFor added in v0.34.0

func RequiredSectionsFor(l *Lesson) []string

RequiredSectionsFor preserves legacy compatibility while exposing the canonical compact Lesson's actual contract to readers.

func ResolveLessonFile

func ResolveLessonFile(lessonsDir, slug string) (string, error)

ResolveLessonFile resolves a canonical directory Lesson first, then a legacy flat file during the compatibility window. Both forms for the same slug are a conflict: picking one would make lifecycle changes nondeterministic.

func RewriteFileAtomic added in v0.34.0

func RewriteFileAtomic(path string, data []byte) error

RewriteFileAtomic replaces an existing Lesson artifact with a same-directory temp, file fsync, atomic rename, and parent-directory fsync. Callers that coordinate with lifecycle writers must hold WithMutationLock across their ownership check, this rewrite, and any shared-index reconciliation.

func Scaffold

func Scaffold(opts ScaffoldOptions) ([]byte, error)

Scaffold returns a lint-clean flat Lesson file body: the artifact-frontmatter-convention frontmatter (`format:` + `status:` mirroring the body `**Status:** Recorded`), the `# Lesson:` title, the body-metadata header, the four required sections (Incident, Process gap, Check, Enforcement) with HTML-comment prompts, and the adherence footer whose URL agrees with `format:`.

A freshly scaffolded Lesson is immediately lint-clean: every required section exists (lint checks presence only, never content), so recording a lesson is a single command with no required flags beyond the slug — the friction-near-zero design this artifact kind depends on for agents to actually use it under time pressure.

func ScaffoldCanonical added in v0.34.0

func ScaffoldCanonical(opts ScaffoldOptions, classifications []string) ([]byte, error)

ScaffoldCanonical returns the compact directory-form Lesson README. The incident diary lives in immutable child occurrences, so the README contains only the durable lesson and its enforceable control.

func ValidateOccurrence added in v0.34.0

func ValidateOccurrence(o Occurrence) error

func ValidateRelation added in v0.34.0

func ValidateRelation(from, typ, to string) error

func ValidateSafeContent added in v0.34.0

func ValidateSafeContent(field, value string) error

ValidateSafeContent enforces the occurrence schema's content policy for all committed Lesson strings. Errors deliberately identify only the field and policy class; unsafe values are never echoed into stderr or logs.

func ValidateSlug

func ValidateSlug(slug string) error

ValidateSlug returns nil when slug is a lowercase, hyphen-separated, URL-safe identifier with no `/`.

func WithMutationLock added in v0.34.0

func WithMutationLock(projectRoot, slug string, mutate func() error) error

WithMutationLock serializes one caller-owned Lesson transaction with every lifecycle, recurrence, relation, and force-update writer. Callers that also mutate the shared index must do so inside mutate: the total order is per-Lesson locks (lexical slug order), the optional relation-project lock, then the shared Lesson-index lock.

func WithMutationLocks added in v0.34.0

func WithMutationLocks(projectRoot string, slugs []string, mutate func() error) error

WithMutationLocks serializes a caller-owned transaction that spans more than one Lesson. Locks are acquired in lexical slug order and duplicate slugs are collapsed, preserving the package-wide lock order before the shared index lock is acquired by mutate.

Types

type AddOccurrenceOptions added in v0.34.0

type AddOccurrenceOptions struct {
	LessonPath string
	// ID is optional for ordinary writes. Deterministic importers may supply a
	// UUID v4-shaped content-derived identity to make replay idempotent.
	ID         string
	Summary    string
	Context    map[string]any
	Evidence   Evidence
	Redactions []string
	Now        time.Time
}

type ChangeStatusOptions

type ChangeStatusOptions struct {
	// SpecRoot is the project root that contains the `spec/` subtree (NOT the
	// `spec/` directory itself). The Lesson is resolved at
	// SpecRoot/spec/lessons/<slug>.md.
	SpecRoot string

	// Slug is the Lesson slug. Caller is expected to have validated it via
	// lesson.ValidateSlug.
	Slug string

	// To is the canonical (title-case) target status.
	To lifecycle.Status

	// Note is the optional free-form markdown transition note, written as a
	// `## Resolution` section. REQUIRED (enforced by the cobra adapter) for
	// the Withdrawn and Superseded dispositions.
	Note string

	// Successor is the slug of the lesson that supersedes this one. REQUIRED
	// (enforced by the cobra adapter) for --to=Superseded, rejected
	// otherwise. Written as a `**Superseded By:** <slug>` header line.
	Successor string

	// PostMutation is the post-rewrite hook (typically a spec-lint pass).
	// Required; ChangeStatus returns exit 10 if nil.
	PostMutation PostMutationHook
}

ChangeStatusOptions packages the inputs to ChangeStatus.

type ChangeStatusResult

type ChangeStatusResult struct {
	Slug string
	From lifecycle.Status
	To   lifecycle.Status
}

ChangeStatusResult is the success payload returned on exit 0. The cobra adapter formats it as the `<slug>: <from> → <to>` success line.

func ChangeStatus

func ChangeStatus(opts ChangeStatusOptions) (ChangeStatusResult, error)

ChangeStatus performs a Lesson-kind lifecycle transition end-to-end.

Flow:

  1. Resolve <slug> to an existing Lesson file. A missing file returns exit 3.
  2. lifecycle.Validate against the KindLesson matrix. Illegal transitions return exit 4.
  3. lifecycle.Rewrite the **Status:** line.
  4. Optionally write the `**Superseded By:**` successor reference and the `## Resolution` note.
  5. Invoke the PostMutation hook. Any failure after step 3 is uncertain and retained for durable recovery; this function never destructively restores a snapshot after publication.

type Evidence added in v0.34.0

type Evidence struct {
	Kind string  `json:"kind"`
	Ref  *string `json:"ref"`
}

type FlatMigrationOptions added in v0.34.0

type FlatMigrationOptions struct {
	LessonsDir      string
	Slug            string
	Classifications []string
	Control         string
	Verification    string
	Evidence        string
	EventUUID       string
}

type FlatMigrationPreflight added in v0.34.0

type FlatMigrationPreflight struct {
	Source             LegacySourceRef
	EventUUID          string
	Classifications    []string
	AlreadyMigrated    bool
	PendingTransaction bool
}

func PreflightFlatMigration added in v0.34.0

func PreflightFlatMigration(opts FlatMigrationOptions) (FlatMigrationPreflight, error)

PreflightFlatMigration validates source identity and the complete generated transaction without publishing committed artifacts. A retry reads the durable marker so the CLI reuses the original event UUID and timestamp.

type FlatMigrationResult added in v0.34.0

type FlatMigrationResult struct {
	CanonicalPath      string   `json:"canonical_path"`
	ManifestPath       string   `json:"manifest_path"`
	CreatedOccurrences []string `json:"created_occurrences"`
	RedactedSections   []string `json:"redacted_sections,omitempty"`
	AlreadyMigrated    bool     `json:"already_migrated"`
	PendingFinalize    bool     `json:"pending_finalize"`
}

func MigrateFlat added in v0.34.0

func MigrateFlat(opts FlatMigrationOptions) (FlatMigrationResult, error)

MigrateFlat migrates exactly one spec/lessons/<slug>.md artifact. A repository-scoped marker makes an interrupted publication inspectable and resumable. All writes are exclusive and every existing path must be either absent or byte-identical before any missing path is published.

type LegacyApplyInspection added in v0.34.0

type LegacyApplyInspection struct {
	MutationRequired bool
	Result           LegacyApplyResult
}

LegacyApplyInspection is the write-free result of validating a reviewed import against its current canonical targets. MutationRequired is false only when every Lesson, occurrence, and manifest is already present and owned by the exact reviewed import.

func InspectLegacyApply added in v0.34.0

func InspectLegacyApply(lessonsDir string, allowedClassifications []string, inv LegacyInventory, mapping LegacyMapping) (LegacyApplyInspection, error)

InspectLegacyApply performs the complete reviewed-import validation and classifies a completed second run without writing even private staging data. Callers must hold every affected per-Lesson lock from inspection through any subsequent ApplyLegacy call.

type LegacyApplyResult added in v0.34.0

type LegacyApplyResult struct {
	CreatedLessons     []string               `json:"created_lessons"`
	CreatedOccurrences []string               `json:"created_occurrences"`
	StatusDecisions    []LegacyStatusDecision `json:"status_decisions"`
	Manual             []string               `json:"manual"`
	Skipped            []string               `json:"skipped"`
	Manifest           string                 `json:"manifest"`
}

func ApplyLegacy added in v0.34.0

func ApplyLegacy(lessonsDir string, allowedClassifications []string, inv LegacyInventory, mapping LegacyMapping) (LegacyApplyResult, error)

type LegacyCandidate added in v0.34.0

type LegacyCandidate struct {
	Kind        string `json:"kind"`
	Line        int    `json:"line"`
	BytesSHA256 string `json:"bytes_sha256"`
}

type LegacyCollision added in v0.34.0

type LegacyCollision struct {
	LegacyID string   `json:"legacy_id"`
	Count    int      `json:"count"`
	Keys     []string `json:"keys"`
}

type LegacyEntry added in v0.34.0

type LegacyEntry struct {
	Key           string   `json:"key"`
	Kind          string   `json:"kind"`
	ParentKey     string   `json:"parent_key,omitempty"`
	LegacyID      string   `json:"legacy_id"`
	Ordinal       int      `json:"ordinal"`
	Title         string   `json:"title"`
	StartLine     int      `json:"start_line"`
	EndLine       int      `json:"end_line"`
	StartByte     int      `json:"start_byte"`
	EndByte       int      `json:"end_byte"`
	RawStatus     string   `json:"raw_status,omitempty"`
	BytesSHA256   string   `json:"bytes_sha256"`
	Raw           string   `json:"-"`
	SuggestedSlug string   `json:"suggested_slug"`
	Warnings      []string `json:"warnings,omitempty"`
}

type LegacyInventory added in v0.34.0

type LegacyInventory struct {
	Source                LegacySourceRef   `json:"source"`
	LessonCount           int               `json:"lesson_count"`
	RecurrenceMarkerCount int               `json:"recurrence_marker_count"`
	EntryProjectionSHA256 string            `json:"entry_projection_sha256"`
	Collisions            []LegacyCollision `json:"collisions"`
	UnmatchedCandidates   []LegacyCandidate `json:"unmatched_candidates"`
	Warnings              []string          `json:"warnings,omitempty"`
	Entries               []LegacyEntry     `json:"entries"`
	// contains filtered or unexported fields
}

func InventoryLegacy added in v0.34.0

func InventoryLegacy(source string) (LegacyInventory, error)

type LegacyMapping added in v0.34.0

type LegacyMapping struct {
	Source  LegacySourceRef      `json:"source"`
	Entries []LegacyMappingEntry `json:"entries"`
}

type LegacyMappingEntry added in v0.34.0

type LegacyMappingEntry struct {
	Key             string   `json:"key"`
	Action          string   `json:"action"`
	Slug            string   `json:"slug,omitempty"`
	Title           string   `json:"title,omitempty"`
	Lesson          string   `json:"lesson,omitempty"`
	ProcessGap      string   `json:"process_gap,omitempty"`
	Status          string   `json:"status,omitempty"`
	Classifications []string `json:"classifications,omitempty"`
}

type LegacySourceRef added in v0.34.0

type LegacySourceRef struct {
	Repository  string `json:"repository"`
	Path        string `json:"path"`
	Revision    string `json:"revision"`
	CommittedAt string `json:"committed_at"`
	SHA256      string `json:"sha256"`
	ByteCount   int    `json:"byte_count"`
}

LegacySourceRef identifies the exact immutable Git blob from which an inventory was derived. Committed migration artifacts retain this reference, byte ranges, and hashes; they never copy the historical prose itself.

type LegacyStatusDecision added in v0.34.0

type LegacyStatusDecision struct {
	Key            string `json:"key"`
	SourceStatus   string `json:"source_status,omitempty"`
	ImportedStatus string `json:"imported_status"`
	Reason         string `json:"reason"`
}

type Lesson

type Lesson struct {
	Path string // absolute path on disk
	Slug string // filename without `.md`
	// Canonical is true when Path is spec/lessons/<slug>/README.md.
	Canonical bool
	// OccurrencesDir is non-empty only for canonical directory lessons.
	OccurrencesDir string

	HasLessonTitle bool   // first H1 line was `# Lesson: <title>`
	TitleLine      int    // 1-based line number of the title (0 when absent)
	Title          string // the `<title>` portion after `# Lesson: `

	Status     string // value of `**Status:**` (empty when missing)
	StatusLine int    // 1-based line of the field; 0 when absent

	Date     string // value of `**Date:**` (empty when missing)
	DateLine int    // 1-based line of the field; 0 when absent

	Owner     string // value of `**Owner:**` (empty when missing)
	OwnerLine int    // 1-based line of the field; 0 when absent

	Classifications      []string
	ClassificationsLine  int
	LegacyProvenance     string
	LegacyProvenanceLine int
	DuplicateOf          string
	DuplicateOfLine      int
	Supersedes           string
	SupersedesLine       int

	Recurred      int    // parsed `**Recurred:**` count; 0 when absent or unparsable
	RecurredRaw   string // raw value as written
	RecurredLine  int    // 1-based line of the field; 0 when absent
	RecurredValid bool   // true when RecurredRaw parsed cleanly as a non-negative integer

	SupersededBy          string // value of `**Superseded By:**` (empty when missing)
	SupersededByLine      int    // 1-based line of the field; 0 when absent
	Control               string
	ControlLine           int
	Verification          string
	VerificationLine      int
	Evidence              string
	EvidenceLine          int
	FrontmatterStatus     string
	FrontmatterStatusLine int
	FieldCounts           map[string]int
	SectionSequence       []string

	// SectionLines maps a present H2 section title to its 1-based heading
	// line. Only sections found in the body appear here; callers check
	// RequiredSections membership against this map's keys to find gaps.
	SectionLines map[string]int
}

Lesson is a parsed single-file Lesson artifact.

func Discover

func Discover(lessonsDir string) ([]*Lesson, error)

Discover walks the direct children of lessonsDir and returns the parsed single-file Lessons found there, sorted alphabetically by Slug.

It selects candidates via IsSingleFileLessonPath (which excludes README.md and anything not directly under lessonsDir), Parses each, and keeps only files whose first H1 was `# Lesson: <title>` (HasLessonTitle == true).

An absent lessonsDir is not an error: Discover returns an empty slice and nil.

func Parse

func Parse(path string) (*Lesson, error)

Parse reads a candidate Lesson file. It returns a populated Lesson even when the file is not actually a Lesson (HasLessonTitle == false in that case) so callers can distinguish "not a Lesson" from "malformed Lesson".

func (*Lesson) HasSection

func (l *Lesson) HasSection(title string) bool

HasSection reports whether title is present in the parsed body as an H2 heading.

func (*Lesson) MissingRequiredSections

func (l *Lesson) MissingRequiredSections() []string

MissingRequiredSections returns the subset of RequiredSections absent from the parsed body, in RequiredSections order. Returns nil when none are missing.

func (*Lesson) MissingRequiredSectionsForLayout added in v0.34.0

func (l *Lesson) MissingRequiredSectionsForLayout() []string

type MutationError added in v0.34.0

type MutationError struct {
	Outcome MutationOutcome
	Err     error
}

MutationError preserves a normal failure while carrying the only safe decision a transaction coordinator may make about its prepared event.

func (*MutationError) Error added in v0.34.0

func (e *MutationError) Error() string

func (*MutationError) Unwrap added in v0.34.0

func (e *MutationError) Unwrap() error

type MutationOutcome added in v0.34.0

type MutationOutcome uint8

MutationOutcome says what this invocation can prove about an artifact write. It deliberately describes the writer's own publication, not whether a path happens to exist: a competing writer may already own that path.

const (
	// MutationPrePublication proves this invocation did not publish an artifact.
	MutationPrePublication MutationOutcome = iota
	// MutationCompensated proves a published artifact was removed and that
	// removal was durably synced.
	MutationCompensated
	// MutationUncertain means publication, removal, or a required durability
	// fence may have happened but cannot be proved either way. Callers must
	// retain their prepared event for explicit reconciliation.
	MutationUncertain
)

func MutationOutcomeOf added in v0.34.0

func MutationOutcomeOf(err error) MutationOutcome

MutationOutcomeOf is conservative for errors from older writers: absent an explicit proof, a caller must assume publication may have survived.

type Occurrence added in v0.34.0

type Occurrence struct {
	SchemaVersion int            `json:"schema_version"`
	ID            string         `json:"id"`
	OccurredAt    time.Time      `json:"occurred_at"`
	Summary       string         `json:"summary"`
	Context       map[string]any `json:"context"`
	Evidence      Evidence       `json:"evidence"`
	Redactions    []string       `json:"redactions"`
	Path          string         `json:"-"`
}

Occurrence is the v1 on-disk JSON contract. Context intentionally remains a typed generic map so the CLI can preserve the format's vendor-neutral shape.

func AddOccurrence added in v0.34.0

func AddOccurrence(opts AddOccurrenceOptions) (Occurrence, error)

AddOccurrence writes exactly one new immutable child JSON file.

func DiscoverOccurrences added in v0.34.0

func DiscoverOccurrences(lessonPath string) ([]Occurrence, error)

DiscoverOccurrences validates every child file and returns deterministic chronological order. A malformed child is an error, never silently ignored.

func FindOccurrence added in v0.34.0

func FindOccurrence(lessonPath, id string) (Occurrence, error)

func ValidateOccurrenceFile added in v0.34.0

func ValidateOccurrenceFile(path string) (Occurrence, error)

ValidateOccurrenceFile validates one child independently so lint can report every malformed child instead of aborting a directory scan at the first.

type PostMutationHook

type PostMutationHook func() error

PostMutationHook is the callback the cobra adapter wires to its bounded index synchronization, durability fence, and read-only validation. Once the status rewrite is visible, a hook failure is retained as MutationUncertain; restoring a whole-file snapshot could erase a concurrent foreign edit.

type Relation added in v0.34.0

type Relation struct {
	From string `json:"from"`
	Type string `json:"type"`
	To   string `json:"to"`
}

func ListRelations added in v0.34.0

func ListRelations(lessonsDir, slug string) ([]Relation, error)

ListRelations returns both metadata relations and directional fields. Its stable ordering makes this also safe for scripting and snapshot tests.

type RelationPostMutationHook added in v0.34.0

type RelationPostMutationHook func() error

RelationPostMutationHook reconciles derived state while every affected per-Lesson lock and the relation-project lock remain held. A CLI hook may acquire only the shared Lesson-index lock, which is last in the total order.

type RelationTransactionHooks added in v0.34.0

type RelationTransactionHooks struct {
	BeforeMutation func() error
	PostMutation   RelationPostMutationHook
	ReconcileNoop  bool
}

RelationTransactionHooks place event preparation immediately before the first visible relation write and reconciliation after it while all endpoint and relation-project locks remain held. ReconcileNoop is reserved for a retry that found the relation complete but still has its original prepared event to finish.

type ScaffoldOptions

type ScaffoldOptions struct {
	Slug  string
	Title string // defaults to a title-cased slug
	Owner string // defaults to "unknown"
	Date  string // ISO-8601 (YYYY-MM-DD); defaults to today's UTC date
}

ScaffoldOptions controls the flat Lesson file Scaffold emits.

Jump to

Keyboard shortcuts

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