saga

package
v0.0.9 Latest Latest
Warning

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

Go to latest
Published: Aug 29, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Index

Constants

View Source
const (
	CurrentVersion = 2
	SchemaURL      = "https://changesaga.dev/schema/v2/saga.schema.json"
)
View Source
const MaxNoteRunes = 2000

MaxNoteRunes bounds sticky note text so a single annotation stays a compact canvas note rather than an unbounded document; schema/v2 enforces the same limit as maxLength.

Variables

This section is empty.

Functions

func ChapterTarget

func ChapterTarget(sagaID, chapterID string) string

func EntrypointError

func EntrypointError(value string) string

EntrypointError reports why a fragment entrypoint is unusable, or "" when it is a well-formed package-relative path.

The check is deliberately expressed in slash-path terms rather than through path/filepath. filepath.Clean rewrites "assets/app.js" to "assets\app.js" on Windows, so an OS-dependent normalization comparison would reject a portable nested entrypoint on exactly one platform. Backslashes are rejected outright because they are an ordinary filename byte on Unix and a separator on Windows, which would make one saga address two different files.

func FragmentTarget

func FragmentTarget(sagaID, fragmentID string) string

func FullLoadCount added in v0.0.8

func FullLoadCount() uint64

FullLoadCount reports process-local full Saga loads. It is diagnostic instrumentation used by scale budgets to keep review mutations off this path; review-only and mutation-index loads do not increment it.

func LandmarkTarget

func LandmarkTarget(sagaID, fragmentID, landmarkID string) string

func Load

func Load(root string) (*Saga, Validation, error)

func LoadMutationIndex added in v0.0.8

func LoadMutationIndex(root string) (MutationIndex, Validation, error)

LoadMutationIndex validates the manifest/package skeleton and returns exact target directories. It is intentionally bounded by authored hierarchy nodes, not by the number of diff mappings or changed atoms.

func LoadNarrative added in v0.0.8

func LoadNarrative(root string) (*Saga, Validation, error)

LoadNarrative reads the complete reviewable narrative and annotations while leaving coverage records and diff-review state unopened. Incremental prose endpoints use it so reaching a chapter or fragment cannot trigger coverage graph construction.

func LoadOutline added in v0.0.8

func LoadOutline(root string) (*Saga, Validation, error)

LoadOutline reads the narrative and review metadata needed to render the reviewer shell without opening coverage records, landmark records, claims, verifications, diff reviews, fragment content, or message attachments. It is deliberately not a replacement for Load: callers that make readiness or mutation decisions must still use the complete validated model.

func LoadReviewState added in v0.0.8

func LoadReviewState(index MutationIndex) (ReviewState, Validation, error)

LoadReviewState reads only review-owned paths named by a validated mutation index. It never opens coverage mappings or ordinary authored fragments.

func LoadTargetDiffs added in v0.0.8

func LoadTargetDiffs(index MutationIndex, target string) ([]DiffFile, Validation, error)

LoadTargetDiffs reads only one validated narrative target's authored evidence. It is the bounded mapping seam used by linked-code requests: no sibling target's ___diffs directory is opened.

func PortabilityWarning

func PortabilityWarning(name string) string

PortabilityWarning reports why a path component cannot exist on every platform Change Saga supports, or "" when the name is portable. It never produces errors: a saga that already contains such a name stays loadable on the platform that created it, but authors are told before publishing it.

func PortablePathWarning

func PortablePathWarning(value string) string

PortablePathWarning applies PortabilityWarning to every component of a slash path.

func SagaTarget

func SagaTarget(sagaID string) string

func SectionTarget

func SectionTarget(sagaID, sectionID string) string

func ValidAnnotationColor

func ValidAnnotationColor(value string) bool

func ValidClaimKind added in v0.0.2

func ValidClaimKind(value string) bool

func ValidID

func ValidID(value string) bool

func ValidMarkdownAnchor

func ValidMarkdownAnchor(value string) bool

func ValidMediaType

func ValidMediaType(value string) bool

ValidMediaType reports whether a fragment media type is one the format defines. Engines render text/markdown, text/html, text/plain, image/svg+xml, and raster image/* fragments.

func ValidVerificationMethod added in v0.0.2

func ValidVerificationMethod(value string) bool

func ValidVerificationStatus added in v0.0.2

func ValidVerificationStatus(value string) bool

func ValidateAnchor

func ValidateAnchor(anchor Anchor) error

Types

type AddedAnchor added in v0.0.2

type AddedAnchor struct {
	Line    int    `json:"line"`
	Heading string `json:"heading"`
	Anchor  string `json:"anchor"`
}

AddedAnchor records one heading that gained a stable anchor.

func FixMarkdownHeadingAnchors added in v0.0.2

func FixMarkdownHeadingAnchors(content []byte, reserved map[string]bool) ([]byte, []AddedAnchor)

FixMarkdownHeadingAnchors appends " {#anchor}" to every heading that does not already declare one and returns the rewritten content. It is deliberately the narrowest possible edit: headings that already carry an anchor keep it, fenced code is skipped, and every other byte — including indentation, blank lines, and CRLF endings — is preserved. Nothing is renamed, so an anchor that a landmark or link already points at cannot move.

reserved names identifiers the generated anchors must avoid, which is how a non-heading landmark keeps its id from being claimed by a heading that would then conflict with it.

type Anchor

type Anchor struct {
	Type       string        `json:"type"`
	Shapes     []Shape       `json:"shapes,omitempty"`
	Text       *TextSelector `json:"text,omitempty"`
	Note       *NoteSelector `json:"note,omitempty"`
	Diff       *DiffSelector `json:"diff,omitempty"`
	Coordinate string        `json:"coordinate_space,omitempty"`
}

type ChapterManifest

type ChapterManifest struct {
	Version int    `json:"version"`
	ID      string `json:"id"`
	Title   string `json:"title"`
	Order   int    `json:"order,omitempty"`
}

type Claim added in v0.0.2

type Claim struct {
	Path      string    `json:"-"`
	Version   int       `json:"version"`
	ID        string    `json:"id"`
	Target    string    `json:"target"`
	Kind      string    `json:"kind"`
	Statement string    `json:"statement"`
	Evidence  []string  `json:"evidence"`
	CreatedAt time.Time `json:"created_at"`
}

Claim is one falsifiable assertion made by the change author. Claims are deliberately independent records: adding a second claim never rewrites the first record and two authors do not contend on one aggregate manifest. Evidence here does not contribute to coverage; it points at exact code that an independent reviewer can inspect when testing the assertion.

type DiffFile

type DiffFile struct {
	Path    string          `json:"-"`
	Version int             `json:"version"`
	Diffs   []DiffReference `json:"diffs"`
}

type DiffReference

type DiffReference struct {
	URI  string `json:"uri"`
	Note string `json:"note,omitempty"`
}

type DiffReview

type DiffReview struct {
	Path              string    `json:"-"`
	AttributionDetail string    `json:"-"`
	Version           int       `json:"version"`
	ID                string    `json:"id"`
	URI               string    `json:"uri"`
	Author            string    `json:"author,omitempty"`
	State             string    `json:"state"`
	CreatedAt         time.Time `json:"created_at"`
}

type DiffSelector

type DiffSelector struct {
	URI string `json:"uri"`
}

type Fragment

type Fragment struct {
	Path       string     `json:"path"`
	Directory  string     `json:"-"`
	ID         string     `json:"id"`
	Title      string     `json:"title,omitempty"`
	MediaType  string     `json:"media_type"`
	Entrypoint string     `json:"entrypoint"`
	Order      int        `json:"order,omitempty"`
	Target     string     `json:"target"`
	Diffs      []DiffFile `json:"diffs,omitempty"`
	HasDiffs   bool       `json:"-"`
	Landmarks  []Landmark `json:"landmarks,omitempty"`
	Reviews    []Review   `json:"reviews,omitempty"`
}

type FragmentManifest

type FragmentManifest struct {
	Version    int    `json:"version"`
	ID         string `json:"id"`
	Title      string `json:"title,omitempty"`
	MediaType  string `json:"media_type"`
	Entrypoint string `json:"entrypoint"`
	Order      int    `json:"order,omitempty"`
}

type Issue

type Issue struct {
	Severity string `json:"severity"`
	Path     string `json:"path"`
	Message  string `json:"message"`
}

type Landmark

type Landmark struct {
	Path        string           `json:"-"`
	Directory   string           `json:"-"`
	Version     int              `json:"version"`
	ID          string           `json:"id"`
	Label       string           `json:"label"`
	Description string           `json:"description,omitempty"`
	Selector    LandmarkSelector `json:"selector"`
	Hotspot     *LandmarkRegion  `json:"hotspot,omitempty"`
	Target      string           `json:"target"`
	Diffs       []DiffFile       `json:"diffs,omitempty"`
	HasDiffs    bool             `json:"-"`
}

type LandmarkRegion

type LandmarkRegion struct {
	X      float64 `json:"x"`
	Y      float64 `json:"y"`
	Width  float64 `json:"width"`
	Height float64 `json:"height"`
}

type LandmarkSelector

type LandmarkSelector struct {
	Type      string  `json:"type"`
	ElementID string  `json:"element_id,omitempty"`
	HeadingID string  `json:"heading_id,omitempty"`
	Exact     string  `json:"exact,omitempty"`
	Prefix    string  `json:"prefix,omitempty"`
	Suffix    string  `json:"suffix,omitempty"`
	X         float64 `json:"x,omitempty"`
	Y         float64 `json:"y,omitempty"`
	Width     float64 `json:"width,omitempty"`
	Height    float64 `json:"height,omitempty"`
}

type Manifest

type Manifest struct {
	Schema  string `json:"$schema,omitempty"`
	Version int    `json:"version"`
	ID      string `json:"id"`
	Title   string `json:"title"`
	PR      *PR    `json:"pr,omitempty"`
	Source  Source `json:"source"`
}

type MarkdownHeading

type MarkdownHeading struct {
	Level    int
	Text     string
	Anchor   string
	Explicit bool
}

MarkdownHeading describes the small heading subset supported by the reference renderer. Explicit anchors use: ## Heading {#stable-anchor}.

func MarkdownHeadings

func MarkdownHeadings(source string) []MarkdownHeading

func ParseMarkdownHeading

func ParseMarkdownHeading(line string) (MarkdownHeading, bool)

type Message

type Message struct {
	Path              string      `json:"-"`
	AttributionDetail string      `json:"-"`
	ID                string      `json:"id"`
	Author            string      `json:"author,omitempty"`
	CreatedAt         time.Time   `json:"created_at"`
	Fragments         []*Fragment `json:"fragments"`
}

type MessageManifest

type MessageManifest struct {
	Version   int       `json:"version"`
	ID        string    `json:"id"`
	Author    string    `json:"author,omitempty"`
	CreatedAt time.Time `json:"created_at"`
}

type MutationIndex added in v0.0.8

type MutationIndex struct {
	Root          string
	Manifest      Manifest
	Targets       map[string]string
	ReviewTargets map[string]string
}

MutationIndex is the small structural contract needed to append review records safely. It reads manifests and package names, never coverage mappings or authored bodies, so a comment does not parse the full saga diff index.

func MutationIndexFromDocument added in v0.0.8

func MutationIndexFromDocument(document *Saga) MutationIndex

MutationIndexFromDocument derives the compact mutation view from an already validated structural generation without touching disk again.

type NoteSelector

type NoteSelector struct {
	Text  string  `json:"text"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Color string  `json:"color,omitempty"`
}

type PR

type PR struct {
	Number *int   `json:"number,omitempty"`
	URL    string `json:"url,omitempty"`
}

PR uses a pointer for Number so an absent pull request number stays absent. schema/v2/saga.schema.json requires a positive number when the field is present; a plain int could not tell "unset" from an invalid literal 0.

type Point

type Point struct {
	X float64 `json:"x"`
	Y float64 `json:"y"`
}

type Review

type Review struct {
	Path              string    `json:"-"`
	AttributionDetail string    `json:"-"`
	Version           int       `json:"version"`
	ID                string    `json:"id"`
	Author            string    `json:"author,omitempty"`
	State             string    `json:"state"`
	Body              string    `json:"body,omitempty"`
	CreatedAt         time.Time `json:"created_at"`
}

type ReviewState added in v0.0.8

type ReviewState struct {
	Threads     []*Thread
	DiffReviews []DiffReview
	ByTarget    map[string][]Review
}

ReviewState is the mutable overlay stored independently from source and coverage indexes.

type Saga

type Saga struct {
	Root          string         `json:"root"`
	Manifest      Manifest       `json:"manifest"`
	Section       *Section       `json:"section"`
	Threads       []*Thread      `json:"threads,omitempty"`
	DiffReviews   []DiffReview   `json:"diff_reviews,omitempty"`
	Claims        []Claim        `json:"claims,omitempty"`
	Verifications []Verification `json:"verifications,omitempty"`
}

type Section

type Section struct {
	Path      string      `json:"path"`
	Kind      string      `json:"kind"`
	ID        string      `json:"id"`
	Title     string      `json:"title"`
	Order     int         `json:"order,omitempty"`
	Target    string      `json:"target"`
	Children  []*Section  `json:"children,omitempty"`
	Fragments []*Fragment `json:"fragments,omitempty"`
	Diffs     []DiffFile  `json:"diffs,omitempty"`
	// HasDiffs records the presence of this target's ___diffs directory without
	// materializing any evidence records. Narrative-only loads use it to render
	// a lazy linked-code affordance while keeping coverage metadata unopened.
	HasDiffs bool     `json:"-"`
	Reviews  []Review `json:"reviews,omitempty"`
}

type SectionManifest

type SectionManifest struct {
	Version int    `json:"version"`
	ID      string `json:"id"`
	Title   string `json:"title"`
	Order   int    `json:"order,omitempty"`
}

type Shape

type Shape struct {
	Type        string  `json:"type"`
	X           float64 `json:"x,omitempty"`
	Y           float64 `json:"y,omitempty"`
	Width       float64 `json:"width,omitempty"`
	Height      float64 `json:"height,omitempty"`
	Points      []Point `json:"points,omitempty"`
	Color       string  `json:"color,omitempty"`
	StrokeWidth float64 `json:"stroke_width,omitempty"`
}

type Source

type Source struct {
	Repository string `json:"repository"`
	Base       string `json:"base"`
	Head       string `json:"head"`
}

type Suggestion

type Suggestion struct {
	Replacement string `json:"replacement"`
}

type TextSelector

type TextSelector struct {
	Exact  string `json:"exact"`
	Prefix string `json:"prefix,omitempty"`
	Suffix string `json:"suffix,omitempty"`
	Start  int    `json:"start,omitempty"`
	End    int    `json:"end,omitempty"`
	Color  string `json:"color,omitempty"`
}

type Thread

type Thread struct {
	Version           int           `json:"version"`
	ID                string        `json:"id"`
	Target            string        `json:"target"`
	Anchor            Anchor        `json:"anchor"`
	Kind              string        `json:"kind,omitempty"`
	Suggestion        *Suggestion   `json:"suggestion,omitempty"`
	CreatedBy         string        `json:"created_by,omitempty"`
	CreatedAt         time.Time     `json:"created_at"`
	Directory         string        `json:"-"`
	AttributionDetail string        `json:"-"`
	Messages          []*Message    `json:"messages,omitempty"`
	Events            []ThreadEvent `json:"events,omitempty"`
	State             string        `json:"state"`
}

type ThreadEvent

type ThreadEvent struct {
	Path              string    `json:"-"`
	AttributionDetail string    `json:"-"`
	Version           int       `json:"version"`
	ID                string    `json:"id"`
	Author            string    `json:"author,omitempty"`
	State             string    `json:"state,omitempty"`
	Anchor            *Anchor   `json:"anchor,omitempty"`
	CreatedAt         time.Time `json:"created_at"`
}

type ThreadManifest

type ThreadManifest struct {
	Version    int         `json:"version"`
	ID         string      `json:"id"`
	Target     string      `json:"target"`
	Anchor     Anchor      `json:"anchor"`
	Kind       string      `json:"kind,omitempty"`
	Suggestion *Suggestion `json:"suggestion,omitempty"`
	CreatedBy  string      `json:"created_by,omitempty"`
	CreatedAt  time.Time   `json:"created_at"`
}

type Validation

type Validation struct {
	Valid  bool    `json:"valid"`
	Issues []Issue `json:"issues"`
}

type Verification added in v0.0.2

type Verification struct {
	Path      string    `json:"-"`
	Version   int       `json:"version"`
	ID        string    `json:"id"`
	Claim     string    `json:"claim"`
	Status    string    `json:"status"`
	Method    string    `json:"method,omitempty"`
	Summary   string    `json:"summary"`
	Command   string    `json:"command,omitempty"`
	CreatedAt time.Time `json:"created_at"`
}

Verification is an append-only result for one claim. The latest result is useful for navigation, but the complete history remains committed as separate files so a later result never erases the earlier one.

Jump to

Keyboard shortcuts

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