Documentation
¶
Index ¶
- Constants
- Variables
- func AddScore(g *Graph, e *Entry, decay DecayFunc, now time.Time) float64
- func AttachDirRelPath(id string) (string, error)
- func CanonicalizeFrontmatter(fm map[string]any) []byte
- func ComputeSkillHash(fileContent []byte) string
- func CountGraphCommits(gitLogOutput string) int
- func DeriveBranchName(entryID, description string) string
- func FormatConfig(cfg Config) string
- func FormatFrontmatter(e *Entry) string
- func FormatSchemaMeta(m SchemaMeta) ([]byte, error)
- func FormatWIPMarker(m *WIPMarker) string
- func GenerateID(typ EntryType, layer Layer, suffix string) string
- func GenerateIDAt(typ EntryType, layer Layer, suffix string, t time.Time) string
- func GenerateWIPMarkerID(participant string) string
- func HeatScore(g *Graph, e *Entry, decay DecayFunc, now time.Time) float64
- func IDToRelPath(id string) (string, error)
- func InDegreeScore(g *Graph, e *Entry) float64
- func IsCapturableRefKind(k RefKind) bool
- func IsDevVersion(v string) bool
- func IsValidKindForType(t EntryType, k Kind) bool
- func IsValidRefKind(k RefKind) bool
- func IsWIPDir(d fs.DirEntry) bool
- func LogScore(g *Graph, e *Entry, decay DecayFunc, now time.Time) float64
- func MultScore(g *Graph, e *Entry, decay DecayFunc, now time.Time) float64
- func ParseMergeTreeConflicts(output string) []string
- func RandomSuffix(n int) (string, error)
- func RefIDs(refs []Ref) []string
- func RelPathToID(rel string) (string, error)
- func RenderSkillFile(entry SkillBundleEntry, version, contentHash string) ([]byte, error)
- func ResolveAttachmentLinks(content, id string) string
- func ResolveSinceSpec(spec string, now time.Time) (time.Time, error)
- func ResolveSyncCooldown(cfg *Config) time.Duration
- func RewriteID(id string, newType EntryType) (string, error)
- func SetYAMLField(existing []byte, path string, value any) ([]byte, error)
- func ShouldBumpMinimumVersion(current, binaryVersion string) bool
- func SkillFileBody(content []byte) []byte
- func SkillInstallDir(target AgentTarget, scope Scope, repoRoot, userHome string) (string, error)
- func ValidateEntry(e *Entry, g *Graph)
- func WIPDir(graphDir string) string
- func WIPMarkerPath(markerID string) string
- type ActorChain
- type AgentTarget
- type AnnotationTopic
- type ArgKind
- type CompatibilityResult
- type Config
- type CountRow
- type Counts
- type DecayFunc
- type EmbeddingConfig
- type Entry
- func (e *Entry) IsActor() bool
- func (e *Entry) IsAnnotation() bool
- func (e *Entry) IsAspiration() bool
- func (e *Entry) IsContract() bool
- func (e *Entry) IsFocus() bool
- func (e *Entry) IsPlan() bool
- func (e *Entry) IsRole() bool
- func (e *Entry) LayerLabel() string
- func (e *Entry) MembersFor(t AnnotationTopic) []string
- func (e *Entry) ResolveActors(inv Involvement) []string
- func (e *Entry) ResolveWhen(inv Involvement) *FocusWhen
- func (e *Entry) ShortContent(maxLen int) string
- func (e *Entry) TypeLabel() string
- type EntryType
- type FlatList
- type FocusBlock
- type FocusGroup
- type FocusState
- type FocusTarget
- type FocusWhen
- type Function
- type FunctionArg
- type Graph
- func (g *Graph) ActiveActorHeads() []*Entry
- func (g *Graph) ActiveFocuses() []*Entry
- func (g *Graph) ActiveRoles() []*Entry
- func (g *Graph) Activities() []*Entry
- func (g *Graph) ActorChains() []*ActorChain
- func (g *Graph) Actors() []*Entry
- func (g *Graph) AllParticipants() []string
- func (g *Graph) Annotations() []*Entry
- func (g *Graph) Aspirations() []*Entry
- func (g *Graph) BuildShowTree(id string, upDepth, downDepth int, rendered, primaries map[string]bool) *ShowTree
- func (g *Graph) ChainForCanonical(canonical string) *ActorChain
- func (g *Graph) ChainsForCanonical(canonical string) []*ActorChain
- func (g *Graph) Contracts() []*Entry
- func (g *Graph) DerivedStatus(e *Entry) Status
- func (g *Graph) Directives() []*Entry
- func (g *Graph) Downstream(id string) []*Entry
- func (g *Graph) EffectiveTopics(e *Entry) []TopicPath
- func (g *Graph) Filter(f GraphFilter) []*Entry
- func (g *Graph) Focuses() []*Entry
- func (g *Graph) GraphDir() string
- func (g *Graph) Lint() []*Entry
- func (g *Graph) OpenSignals() []*Entry
- func (g *Graph) Plans() []*Entry
- func (g *Graph) RecentDone(n int) []*Entry
- func (g *Graph) RecentInsights(n int) []*Entry
- func (g *Graph) RefChain(id string) []*Entry
- func (g *Graph) ResolveID(input string) (string, error)
- func (g *Graph) ResolveIDs(inputs []string) ([]string, error)
- func (g *Graph) ResolveRef(id string) ResolvedRef
- func (g *Graph) ResolveRefIDs(refs []Ref) ([]Ref, error)
- func (g *Graph) ResolveRoleChain(role *Entry) *ActorChain
- func (g *Graph) Roles() []*Entry
- func (g *Graph) SetGraphDir(dir string)
- func (g *Graph) TopicCounts(entries []*Entry, now time.Time) Counts
- func (g *Graph) TopologicalOrder() []*Entry
- type GraphFilter
- type Group
- type Grouped
- type IDParts
- type Involvement
- type Kind
- type LLMConfig
- type Layer
- type Layout
- type ModelRollup
- type OpRollup
- type ParticipantsBlock
- type ParticipantsGroup
- type Ref
- type RefExpansion
- type RefKind
- type RefKindCell
- type RefTargetClass
- type RenderShape
- type ResolvedRef
- type RetryConfig
- type SchemaMeta
- type Scope
- type Section
- type SectionData
- type ShowTree
- type ShowTreeItem
- type SkillBundle
- type SkillBundleEntry
- type SkillFile
- type SkillInstallStatus
- type StatMetrics
- type StatsRecord
- type StatsReport
- type Status
- type StatusKind
- type SyncConfig
- type SyncState
- type SyncStatus
- type TopicPath
- type TruncatedRef
- type WIPMarker
- type Warning
- type WipList
Constants ¶
const ( // DefaultGraphDir is the conventional graph directory relative to repo root // when initialized with sdd init. DefaultGraphDir = ".sdd/graph" // SDDDirName is the metadata directory name at the repository root. SDDDirName = ".sdd" // DefaultLLMProvider is the provider used when none is configured. The // claude CLI bridge runs via the user's logged-in Claude Code session, so // no API key is required for first-run usage. DefaultLLMProvider = "claude-cli" // DefaultLLMModel is the claude model used when none is configured. // Sonnet-class, matching the model the pre-flight calibration eval // measures — pre-flight is the highest-stakes LLM call, and a blocking // validator should not run on a cheaper tier than it was calibrated // against (d-tac-b30). DefaultLLMModel = "claude-sonnet-4-6" // DefaultLLMConcurrency is the default worker count for concurrent // LLM calls (e.g. sdd summarize --all). DefaultLLMConcurrency = 4 // DefaultRetryMaxAttempts is the total number of LLM call attempts // (1 = no retry) when the gollm runner hits a transient failure. Five // attempts ride out a connection blip or a short 429 without making an // interactive sdd new hang indefinitely. DefaultRetryMaxAttempts = 5 // DefaultRetryBaseDelay is the exponential-backoff floor between retries. DefaultRetryBaseDelay = "2s" // DefaultRetryMaxDelay caps backoff growth between retries. DefaultRetryMaxDelay = "60s" // DefaultSyncCooldown bounds how often background sync runs git fetch // when last-fetch exceeds this duration. Applied when Config.Sync.Cooldown // is empty or unparseable. DefaultSyncCooldown = "15m" )
const ( SkillStampVersion = "sdd-version" SkillStampHash = "sdd-content-hash" )
Stamp frontmatter keys injected into every installed skill file. Stripped before hashing so the stamps themselves don't pollute the content hash.
const CurrentGraphSchemaVersion = 1
CurrentGraphSchemaVersion is the schema version this binary knows how to read and write. Bumped only by an explicit schema migration.
const DefaultAgentTarget = AgentClaude
DefaultAgentTarget is the target used when nothing is specified. Only one agent is wired at MVP.
const DefaultDecayName = "exp-14d"
DefaultDecayName is the decay used when an algorithm is invoked without an explicit decay arg (e.g. `rank(heat)` resolves to heat(exp-14d)).
const DefaultScope = ScopeUser
DefaultScope is the scope used when nothing is specified.
const SchemaMetaFileName = "meta.json"
SchemaMetaFileName is the committed metadata file under .sdd/ that records graph schema version and the oldest binary permitted to write to the graph.
Variables ¶
var LayerAbbrev = map[Layer]string{ LayerStrategic: "stg", LayerConceptual: "cpt", LayerTactical: "tac", LayerOperational: "ops", LayerProcess: "prc", }
LayerAbbrev maps full layer names to abbreviations used in IDs.
var LayerFromAbbrev = map[string]Layer{ "stg": LayerStrategic, "cpt": LayerConceptual, "tac": LayerTactical, "ops": LayerOperational, "prc": LayerProcess, }
LayerFromAbbrev maps abbreviations to full layer names.
var TypeAbbrev = map[EntryType]string{ TypeSignal: "s", TypeDecision: "d", }
TypeAbbrev maps full type names to abbreviations used in IDs.
var TypeFromAbbrev = map[string]EntryType{ "s": TypeSignal, "d": TypeDecision, }
TypeFromAbbrev maps abbreviations to full type names.
Functions ¶
func AddScore ¶ added in v0.5.0
AddScore is heat + in-degree. Entries that are recent OR structurally central both rise; the dimensions contribute additively.
The d-tac-uww spec calls for `normalized(heat + in-degree)` without pinning down the normalization strategy. Slice 3 uses raw sum as the simplest defensible choice; the comparative findings attached to the closing done signal will inform whether to scale either dimension.
func AttachDirRelPath ¶
AttachDirRelPath returns the relative path to the attachment directory for an entry ID. This is the entry's file path without the .md extension.
func CanonicalizeFrontmatter ¶ added in v0.2.0
CanonicalizeFrontmatter returns a deterministic byte representation of fm suitable for hashing. The encoding is JSON with sorted keys; json.Marshal on map[string]any sorts keys alphabetically at every depth, giving a stable ordering across YAML read/write cycles that don't preserve order.
The output has no trailing newline. Callers embedding it in a larger hash stream must add their own separator.
func ComputeSkillHash ¶ added in v0.2.0
ComputeSkillHash returns the canonical content hash for a skill file. The hash is computed over the canonicalized frontmatter (with the sdd-version and sdd-content-hash keys stripped) concatenated with the body bytes. The result is a lowercase hex-encoded sha256 digest.
Embedded entries carry no stamps, so their hash equals a freshly-written file's computed hash — this is the equality that lets a previously-installed pristine file match its embedded source across version bumps.
func CountGraphCommits ¶ added in v0.3.0
CountGraphCommits counts non-empty lines from `git log --grep='^sdd:' --pretty=format:%H <range>`. Each line is one SDD-written commit in the range; we only need the count, not the hashes themselves.
func DeriveBranchName ¶
DeriveBranchName creates a branch name from an entry ID and description. Format: sdd/<entry-suffix>-<description-slug>
func FormatConfig ¶
FormatConfig returns a commented YAML config template with the given graph dir. If cfg.Language is set, the locale is written as an active `language: <code>` entry. Otherwise a commented hint is emitted instead so the option stays discoverable in the file.
func FormatFrontmatter ¶
FormatFrontmatter creates the YAML frontmatter string for an entry.
func FormatSchemaMeta ¶ added in v0.2.0
func FormatSchemaMeta(m SchemaMeta) ([]byte, error)
FormatSchemaMeta returns a pretty-printed JSON representation with a trailing newline, suitable for writing to disk.
func FormatWIPMarker ¶
FormatWIPMarker creates the file content for a WIP marker.
func GenerateID ¶
GenerateID creates a new document ID with the current timestamp and a random suffix.
func GenerateIDAt ¶
GenerateIDAt creates a new document ID with the given timestamp and a random suffix. Accepts the time explicitly so callers can inject a clock for testability.
func GenerateWIPMarkerID ¶
GenerateWIPMarkerID creates a marker ID from the current timestamp and participant name.
func HeatScore ¶ added in v0.5.0
HeatScore computes the recency-weighted in-degree of an entry: each incoming reference contributes `decay(ageDays(ref_source))` to the score, where ageDays is the gap between `now` and the referencing entry's creation time. Default decay is exp-14d when callers don't override (see DefaultDecayName).
Heat is the foundational rank signal: an entry referenced by many recent entries has high heat; an entry referenced only by old entries has low heat regardless of in-degree.
func IDToRelPath ¶
IDToRelPath converts an entry ID to its relative file path in the hierarchical layout. ID format: YYYYMMDD-HHmmss-type-layer-suffix Path format: YYYY/MM/DD-HHmmss-type-layer-suffix.md
func InDegreeScore ¶ added in v0.5.0
InDegreeScore returns the raw count of incoming references — purely structural centrality with no recency weighting. Equivalent to HeatScore with the `none` decay.
func IsCapturableRefKind ¶ added in v0.6.0
IsCapturableRefKind reports whether k is a valid kind for a new entry. Excludes RefKindUnknown — that sentinel only exists for legacy bare-string refs already on disk and is rejected at capture pre-flight.
func IsDevVersion ¶ added in v0.2.0
IsDevVersion reports whether v is a non-release build. Dev builds (including the literal "dev", commit hashes, or any non-semver string) bypass write gates to avoid blocking local development against a released graph.
func IsValidKindForType ¶ added in v0.2.0
IsValidKindForType reports whether k is an allowed kind for the given type. Empty kind is allowed at this layer — defaults are applied separately during entry construction (see DefaultKindForType).
func IsValidRefKind ¶ added in v0.6.0
IsValidRefKind reports whether k is one of the closed-set kind values, including the legacy unknown sentinel. Parse-time and graph-traversal callers use this; capture-time callers (pre-flight) use IsCapturableRefKind to additionally reject unknown.
func LogScore ¶ added in v0.5.0
LogScore is heat × log(1 + in-degree). Diminishing returns on in-degree softens the bias toward heavily-referenced entries while still rewarding recency.
func MultScore ¶ added in v0.5.0
MultScore is heat × in-degree. Entries that are both recent (high heat) and structurally central (high in-degree) rise to the top; either dimension at zero zeroes the product.
func ParseMergeTreeConflicts ¶ added in v0.3.0
ParseMergeTreeConflicts parses the output of `git merge-tree --write-tree --name-only --no-messages HEAD @{u}`. On a clean merge, output is a single tree-OID line → returns nil. On a conflict, output is the tree OID followed by conflicted file paths → returns the paths. A blank line terminates the path list; any trailing informational/conflict-message section (present when --no-messages is omitted) is ignored, so the parser stays correct even if the caller forgets the flag.
func RandomSuffix ¶
RandomSuffix returns an n-character lowercase alphanumeric string suitable for use as the trailing random portion of a document ID.
func RefIDs ¶ added in v0.6.0
RefIDs extracts the bare ID strings from a slice of Refs. Traversal, summary rendering, and validator code that only cares about identity uses this helper instead of inlining the loop.
func RelPathToID ¶
RelPathToID converts a relative path (YYYY/MM/DD-HHmmss-type-layer-suffix.md) to a full entry ID.
func RenderSkillFile ¶ added in v0.2.0
func RenderSkillFile(entry SkillBundleEntry, version, contentHash string) ([]byte, error)
RenderSkillFile reassembles frontmatter and body into a skill file with the given install-time stamps injected. If the entry has no original frontmatter, a fresh block with just the stamps is written.
The returned bytes are what gets hashed by ComputeSkillHash once the stamps are stripped — callers writing new installations should compute the hash from the embedded entry content (which has no stamps) to populate the sdd-content-hash stamp itself.
func ResolveAttachmentLinks ¶
ResolveAttachmentLinks replaces {{attachments}} placeholders in content with the actual relative directory path for markdown links.
func ResolveSinceSpec ¶ added in v0.5.0
ResolveSinceSpec parses a since() argument into an absolute cutoff time. Entries with creation time on or after the cutoff pass the filter; entries before are excluded.
Accepts two forms per d-tac-uww §4:
- ISO date: YYYY-MM-DD — interpreted as midnight UTC of that day.
- Duration: Nd | Nw | Nm | Ny — N is a non-negative integer. `d` and `w` use exact 24h offsets (1d = 24h, 1w = 7×24h). `m` and `y` use calendar arithmetic via time.AddDate so month/ year boundaries match human expectations regardless of leap days or month length.
`now` is the reference time the duration form subtracts from. Tests supply a fixed clock; the executor passes time.Now().
func ResolveSyncCooldown ¶ added in v0.3.0
ResolveSyncCooldown returns the effective cooldown duration from cfg, falling back to DefaultSyncCooldown on empty or unparseable values.
func RewriteID ¶ added in v0.2.0
RewriteID returns the id with its type abbreviation replaced by newType's abbreviation. Timestamp, layer, and suffix are preserved. Used by the sdd rewrite command for mechanical type changes.
func SetYAMLField ¶ added in v0.3.0
SetYAMLField sets the scalar at dotted path to value in an existing YAML document, returning new bytes. Unknown keys, user comments, and the order of sibling keys are preserved — only the targeted scalar (or the minimal set of missing ancestors) is touched. An empty input is treated as an empty document; the result is a fresh mapping containing just the target key chain.
Value may be anything yaml.Node.Encode accepts as a scalar (string, int, bool, nil, etc.). Sequences and nested mappings are out of scope — targeting a non-scalar leaf (or a slice/map value) is rejected.
Path uses dotted segments (e.g. "participant", "llm.provider"). No support for array indexing or filter expressions — keep paths flat. Empty segments (leading/trailing/consecutive dots) are rejected.
The port of the JSONPath-capable vignet patcher was intentional: SDD's settings are flat config, so the simpler dotted-path form covers the need without the extra dependency.
func ShouldBumpMinimumVersion ¶ added in v0.5.1
ShouldBumpMinimumVersion reports whether a recorded minimum_version value should be raised to match a running binary. Returns true when the binary is a released semver strictly higher than current, when current is empty (no floor recorded), or when current is malformed. Returns false for dev binaries — callers should reject the bump path before reaching this check, but the helper is conservative.
func SkillFileBody ¶ added in v0.7.0
SkillFileBody returns the body of a skill file (frontmatter stripped). Bundle sources carry no install stamps, so this is usually the whole content; the strip keeps it correct if a fragment ever grows frontmatter.
func SkillInstallDir ¶ added in v0.2.0
func SkillInstallDir(target AgentTarget, scope Scope, repoRoot, userHome string) (string, error)
SkillInstallDir returns the absolute directory where skills install for a given agent target and scope.
Claude skills install at <home>/.claude/skills (user scope) or <repoRoot>/.claude/skills (project scope). The function errors if the required base path is empty for the chosen scope — callers should populate UserHome or RepoRoot before the query reaches the finder.
func ValidateEntry ¶
ValidateEntry checks a single entry for integrity issues and populates its Warnings field. Used both at lint time (all entries) and at write time (new entry before commit).
func WIPMarkerPath ¶
WIPMarkerPath returns the path to a marker file relative to the graph directory.
Types ¶
type ActorChain ¶ added in v0.3.0
type ActorChain struct {
// Entries are all actor signals in the chain, ordered by time (oldest
// first).
Entries []*Entry
// Head is the chain's current head — the actor signal not superseded
// by any other entry in the chain. Capture-time checks resolve against
// the head's canonical; derivation-time checks walk CanonicalHistory.
Head *Entry
// CanonicalHistory is the ordered list of distinct canonicals ever
// carried by entries in this chain, oldest first.
CanonicalHistory []string
// contains filtered or unexported fields
}
ActorChain represents a supersession chain of kind: actor signals — all actor entries linked by supersedes (transitively) plus their shared canonical history. Canonicals within a chain may change across entries (e.g., typo correction supersedes an earlier spelling); across chains each canonical is write-once (see validateActorInvariant in lint).
func (*ActorChain) HasCanonical ¶ added in v0.3.0
func (c *ActorChain) HasCanonical(name string) bool
HasCanonical reports whether the chain has ever held canonical c.
type AgentTarget ¶ added in v0.2.0
type AgentTarget string
AgentTarget identifies which agent a skill bundle installs for. Claude is the only supported target for MVP; the type exists so the install flow can grow additional agents (Codex, etc.) without structural change.
const ( // AgentClaude installs into Claude Code's skill directories // (~/.claude/skills or <repo>/.claude/skills). AgentClaude AgentTarget = "claude" )
type AnnotationTopic ¶ added in v0.5.0
AnnotationTopic is one topic assignment carried by a kind: annotation entry's topics:[] frontmatter list. Two YAML shapes are supported per item:
- Plain string: "<label>" — applies to all of the annotation's refs. Members is nil in this case.
- Mapping: {label: <string>, members: [<id>, ...]} — applies the topic to the listed members specifically. Members must be a subset of the annotation's refs (validated at handler / pre-flight time, not here).
The label string must be a valid TopicPath; parsing happens at consumption time (display rendering, topic filter), not here, so an invalid label surfaces with a precise error rather than corrupting the entry's other fields at load time.
func (AnnotationTopic) MarshalYAML ¶ added in v0.5.0
func (a AnnotationTopic) MarshalYAML() (any, error)
MarshalYAML emits the most compact form: a scalar string when no members are set, or a mapping otherwise.
func (*AnnotationTopic) UnmarshalYAML ¶ added in v0.5.0
func (a *AnnotationTopic) UnmarshalYAML(node *yaml.Node) error
UnmarshalYAML accepts either a scalar string (plain label form) or a mapping with `label` and optional `members` keys.
type ArgKind ¶ added in v0.5.0
type ArgKind string
ArgKind tags the variant of a FunctionArg. Args are either nested function calls (e.g. `heat(exp-14d)` inside `rank(...)`) or literal values (numbers, bare identifiers, quoted strings).
const ( // ArgKindFunc is a nested function call; FunctionArg.Func is populated. ArgKindFunc ArgKind = "func" // ArgKindNumber is a numeric literal; FunctionArg.Number is populated. // Floats are accepted at parse time so future modifiers like // stalled(0.5) don't churn the type; integer-only consumers (e.g. n(N)) // validate integrality at execute time. ArgKindNumber ArgKind = "number" // ArgKindIdent is a bare identifier (unquoted, e.g. `plan`, `exp-14d`); // FunctionArg.String holds the raw text. ArgKindIdent ArgKind = "ident" // ArgKindString is a quoted string literal; FunctionArg.String holds // the unquoted content. ArgKindString ArgKind = "string" )
type CompatibilityResult ¶ added in v0.2.0
type CompatibilityResult struct {
// Compatible is true when the binary may proceed. Dev builds are always
// compatible (see IsDevBuild).
Compatible bool
// IsDevBuild is true when the binary version is non-semver; both gates
// are bypassed.
IsDevBuild bool
// Reason is a human-readable explanation when !Compatible.
Reason string
}
CompatibilityResult reports whether a binary is permitted to write to a graph given its .sdd/meta.json contents.
func CheckCompatibility ¶ added in v0.2.0
func CheckCompatibility(meta SchemaMeta, binaryVersion string, binarySchemaVersion int) CompatibilityResult
CheckCompatibility evaluates meta against the binary's own version and declared schema version.
Dev builds (non-semver binary versions) are always compatible; both gates are bypassed so local development isn't blocked by a released graph's minimum_version or a bumped schema_version.
For released binaries: a schema_version mismatch errors with a pointer to sdd init / sdd migrate. A minimum_version higher than the binary errors with upgrade guidance. Either failure leaves Compatible = false.
type Config ¶
type Config struct {
GraphDir string `yaml:"graph_dir,omitempty"`
LLM LLMConfig `yaml:"llm,omitempty"`
Embedding EmbeddingConfig `yaml:"embedding,omitempty"`
Sync SyncConfig `yaml:"sync,omitempty"`
// Participant is the canonical name used for entry authorship when
// --participants / --participant is omitted at capture time. Lives in
// .sdd/config.local.yaml (gitignored) because the same person may use
// different spellings across projects.
Participant string `yaml:"participant,omitempty"`
// Language is a locale code (e.g. "de", "en", "de-DE") that governs the
// graph's authored language. Captured entries are written in this
// language; the /sdd skill renders translated vocabulary to users via
// bundled translation references. Empty means English (default).
Language string `yaml:"language,omitempty"`
// SkillScope records where the project's skills were installed: user
// (~/.claude/skills/) or project (.claude/skills/). `sdd init` writes
// it the first time scope is chosen and reads it on every subsequent
// run so the installed location stays stable for every contributor on
// the repo. Empty means "no recorded preference" — typical of graphs
// initialized before the readiness-check work landed.
SkillScope Scope `yaml:"skill_scope,omitempty"`
}
Config represents the contents of .sdd/config.yaml (shared, committed) or .sdd/config.local.yaml (gitignored, per-machine). Both files unmarshal into the same struct; the local file overlays the shared file via MergeConfig. Empty / zero-valued fields in the local file mean "inherit from shared", so any subset of fields can appear in either file.
func MergeConfig ¶ added in v0.2.0
MergeConfig returns a new Config with fields from overlay overriding base wherever the overlay value is non-empty/non-zero. APIKeys are merged key-by-key so overlay entries replace individual providers without clobbering the full map. A nil overlay returns a copy of base.
func ParseConfig ¶
ParseConfig unmarshals YAML bytes into a Config struct. Empty input is valid and yields a zero-valued Config.
type CountRow ¶ added in v0.7.0
CountRow is one aggregated topic row in a Counts result: the topic Label (first-seen casing), how many entries in the filtered set carry it as an effective topic, and the summed heat contribution of those entries.
type Counts ¶ added in v0.7.0
type Counts struct {
Rows []CountRow
}
Counts is the result shape produced by `as-counts`. It answers "what topics exist and how many entries each carries" without enumerating members — the mechanical lookup the capture-time topic procedure leans on instead of per-run agent inference. Rows are ordered by the finder (count descending, then heat descending, then label) so the busiest clusters surface first.
Entries with no effective topics contribute to no row; surface them with the `untagged` filter instead.
func (Counts) Shape ¶ added in v0.7.0
func (Counts) Shape() RenderShape
Shape implements SectionData.
type DecayFunc ¶ added in v0.5.0
DecayFunc maps an entry age in days to a recency weight in [0, 1]. Used by ranking algorithms to weight an entry's incoming references by recency: a recent reference contributes more to "heat" than an old one.
func DecayByName ¶ added in v0.5.0
DecayFunc resolves a decay name to its function per d-tac-uww §4. Returns an error for unknown names with the listed valid set.
Vocabulary:
exp-{7,14,30}d 2^(-age_days/N) — half-life every N days
linear-{7,14,30}d max(0, 1 - age_days/N) — zero past N days
none 1 — no age effect
Exponential decay never reaches zero, so historical references retain vestigial weight. Linear decay reaches zero at the window edge, so references older than the window contribute nothing — useful for strict recency cutoffs.
type EmbeddingConfig ¶ added in v0.4.0
type EmbeddingConfig struct {
// Provider names the embedder transport: "openai" (OpenAI-compatible
// /v1/embeddings) or "ollama" (/api/embeddings). Empty disables vector
// search — the CLI falls back to text-mode-only.
Provider string `yaml:"provider,omitempty"`
// Model is the provider-specific embedding model identifier
// (e.g. "text-embedding-3-small", "nomic-embed-text").
Model string `yaml:"model,omitempty"`
// Endpoint overrides the OpenAI-compatible base URL. Empty defaults to
// the OpenAI public endpoint. Useful for self-hosted proxies that
// implement the same wire protocol.
Endpoint string `yaml:"endpoint,omitempty"`
// OllamaEndpoint overrides the default Ollama URL for the embedding
// adapter. Independent of LLMConfig.OllamaEndpoint so embedding and
// chat can target different instances.
OllamaEndpoint string `yaml:"ollama_endpoint,omitempty"`
// APIKeys maps provider name to API key. Defaults to LLMConfig.APIKeys
// if empty so a single key value can serve both axes; explicit values
// here override that.
APIKeys map[string]string `yaml:"api_keys,omitempty"`
// RateLimitRPS caps remote-provider requests per second. Zero means
// "apply a conservative per-provider default safe for tier-1 limits".
// Local providers (ollama) ignore this field.
RateLimitRPS float64 `yaml:"rate_limit_rps,omitempty"`
// Timeout is a Go duration string (e.g. "30s") applied per Embed call.
Timeout string `yaml:"timeout,omitempty"`
// Dimensions optionally overrides the embedded vector size — used for
// OpenAI's matryoshka-style truncation. Zero means "use the model's
// native dimension".
Dimensions int `yaml:"dimensions,omitempty"`
// BatchSize bounds the number of inputs sent in a single embedding
// request. Zero means "use a provider-specific default" (100 for
// openai, 64 for ollama). Override when working with very large or
// very small inputs to balance throughput against per-call timeout.
BatchSize int `yaml:"batch_size,omitempty"`
// QueryTemplate is applied to every text passed through
// EmbedQueries before the transport call. The literal `{text}` is
// replaced with the input. Empty disables the transformation
// (matches OpenAI's prefix-agnostic behavior). Used by retrieval
// (sdd search). Changing this is a free-tweak — query template
// changes do not invalidate indexed embeddings.
//
// Example for Qwen3 / instruction-tuned encoders:
//
// query_template: |-
// Instruct: Given a query phrase, retrieve related entries from a knowledge graph
// Query:{text}
QueryTemplate string `yaml:"query_template,omitempty"`
// DocumentTemplate is applied to every text passed through
// EmbedDocuments before the transport call. Same `{text}`
// substitution as QueryTemplate. Empty disables. Used by indexing
// (sdd index, sdd search lazy-fill).
//
// Document templates feed into the embedder Fingerprint — changing
// this invalidates the index and triggers re-embed on the next
// search (or eagerly via `sdd index --force`). Required for
// dual-prefix models (E5 `passage:`, Nomic `search_document:`);
// stays empty for query-only models (Qwen3, BGE) and untemplated
// models (OpenAI).
DocumentTemplate string `yaml:"document_template,omitempty"`
}
EmbeddingConfig holds settings for the search index's embedding provider. Decoupled from LLMConfig (chat / summary) so a participant can run a local Ollama embedder while still using a remote chat provider, and vice versa. Lives in `.sdd/config.local.yaml` because indexes are per-participant (see d-tac-lqr's storage decision).
type Entry ¶
type Entry struct {
ID string
Type EntryType
Layer Layer
Kind Kind // only meaningful for decisions; empty = directive (default)
Refs []Ref
Supersedes []string
Closes []string
Participants []string
Confidence string
Content string
Time time.Time
// Canonical and Aliases are only meaningful on kind: actor signals.
// Canonical is the write-once identity string used in participants fields;
// Aliases are read-side conveniences for mining and dialogue comprehension.
Canonical string
Aliases []string
// Actor is only meaningful on kind: role decisions. It names the canonical
// of the actor-identity chain the role binds to. Role status derives from
// the actor chain's canonical history (see Graph.RoleStatus).
Actor string
// Topics carries inline topic labels — valid on any non-annotation entry.
// Empty for kind: annotation entries (those use AnnotationTopics, which
// supports the richer "label or {label, members}" form). Each path is
// parsed and validated at load time; invalid components surface as
// Warnings rather than failing the parse, matching how other shape rules
// are handled.
Topics []TopicPath
// AnnotationTopics carries the topic assignments declared by a
// kind: annotation entry. Each item is either a plain label (Members nil
// — applies to all of the annotation's Refs) or a label with explicit
// member sub-selection (Members must be a subset of Refs; checked at
// pre-flight time).
AnnotationTopics []AnnotationTopic
// FocusActors is the focus-level default actor list (canonical-only),
// used for involvement triples that don't carry their own actors override.
// Only meaningful on kind: focus decisions.
FocusActors []string
// FocusWhen is the focus-level default temporal scope. Per-involvement
// `when:` overrides this; involvement without `when:` inherits this value.
// Only meaningful on kind: focus decisions.
FocusWhen *FocusWhen
// Involvement is the list of involvement triples on a kind: focus
// decision. Each triple binds a target entry to (resolved) actors and
// (resolved) when scope.
Involvement []Involvement
Preflight string // "skipped" or "error" annotation from pre-flight validation
Attachments []string // filenames discovered from the co-located attachment directory
Summary string // LLM-generated summary: this entry + direct relationships
SummaryHash string // hex-encoded hash of the rendered summary prompt inputs
Warnings []Warning // validation issues found during graph construction
}
func ParseEntry ¶
ParseEntry parses a graph entry from its filename and file content.
func (*Entry) IsActor ¶ added in v0.3.0
IsActor returns true if this signal records a participant identity.
func (*Entry) IsAnnotation ¶ added in v0.5.0
IsAnnotation reports whether this signal records a structural topic annotation. Annotation entries are excluded from catch-up narrative rendering and surface only via topic queries.
func (*Entry) IsAspiration ¶ added in v0.2.0
IsAspiration returns true if this decision is a perpetual direction.
func (*Entry) IsContract ¶
IsContract returns true if this decision is a standing constraint.
func (*Entry) IsFocus ¶ added in v0.5.0
IsFocus reports whether this decision is a focus declaration — a dual-lifecycle entry carrying involvement triples.
func (*Entry) IsRole ¶ added in v0.3.0
IsRole returns true if this decision commits a participation pattern.
func (*Entry) LayerLabel ¶
LayerLabel returns a display label for the layer.
func (*Entry) MembersFor ¶ added in v0.5.0
func (e *Entry) MembersFor(t AnnotationTopic) []string
MembersFor returns the entry IDs the annotation assigns to topic at index i — explicit members if set, otherwise the annotation's own refs (since the plain-string item form means "applies to all refs").
func (*Entry) ResolveActors ¶ added in v0.5.0
func (e *Entry) ResolveActors(inv Involvement) []string
ResolveActors returns the effective actor canonicals for an involvement target on a focus entry: the involvement's own Actors if set (including the explicit empty case), else the focus-level default (e.IsActors), else nil. The empty case carries semantic weight — "pull-available" — so callers must distinguish nil from len()==0 by checking ActorsSet.
func (*Entry) ResolveWhen ¶ added in v0.5.0
func (e *Entry) ResolveWhen(inv Involvement) *FocusWhen
ResolveWhen returns the effective temporal scope for an involvement — per-involvement When if set, else the focus-level default, else nil.
func (*Entry) ShortContent ¶
ShortContent returns content truncated to maxLen, preferring sentence boundaries. Accumulates complete sentences up to the limit. If no sentence fits, accumulates words.
type FlatList ¶ added in v0.5.0
type FlatList struct {
Entries []*Entry
Scores []float64
RefExpansions [][]RefExpansion
}
FlatList is an ordered sequence of entries — the shape consumed by the as-list presenter. Used for top(N), topic(L), and any pipeline that ends in as-list.
Scores is parallel to Entries when the section was ranked: Scores[i] is the rank score of Entries[i]. When unranked (no rank() in the pipeline, or by(date) which sorts without scoring), Scores is nil. The renderer detects ranked output via len(Scores) == len(Entries).
RefExpansions is parallel to Entries when the section used expand(refs): RefExpansions[i] is the ordered list of resolved outgoing refs for Entries[i] (possibly empty for an entry with no surviving refs). When expand(refs) is absent, RefExpansions is nil. The renderer detects expanded output via len(RefExpansions) == len(Entries).
func (FlatList) Shape ¶ added in v0.5.0
func (FlatList) Shape() RenderShape
Shape implements SectionData.
type FocusBlock ¶ added in v0.5.0
type FocusBlock struct {
Focuses []FocusGroup
}
FocusBlock is a list of FocusGroup entries, one per focus decision in the input, in the order they appeared. Each FocusGroup carries its target rows already filtered for omission (closed/superseded targets dropped per design §6) and ordered as listed in the focus's involvement frontmatter.
func (FocusBlock) Shape ¶ added in v0.5.0
func (FocusBlock) Shape() RenderShape
Shape implements SectionData.
type FocusGroup ¶ added in v0.5.0
type FocusGroup struct {
Focus *Entry
Actors []string // focus-level default actors (canonical-only)
When *FocusWhen // focus-level default temporal scope
Targets []FocusTarget
}
FocusGroup is one focus decision plus its resolved involvement targets. Defaults (Actors, When) are carried explicitly so the renderer can show "inherited from focus" cleanly without re-resolving per target.
type FocusState ¶ added in v0.5.0
type FocusState string
FocusState classifies an involvement target's engagement level for the focus-block render. Slice 7 derives state from the resolved actors set and the target's heat under the configured rank, per the algorithm in d-tac-uww §6.
const ( // FocusStatePullAvailable — target's resolved actors set is empty, // either because the involvement explicitly declared `actors: []` // or the focus-level default is empty. The target is in scope but // awaiting pickup. FocusStatePullAvailable FocusState = "pull-available" // FocusStateStalled — target has actors assigned but its heat under // the section's rank is below the configured stalled threshold. // Signal that the work is dormant despite being attributed. FocusStateStalled FocusState = "stalled" // FocusStateDriving — target has actors and is engaged (heat above // the stalled threshold). The default expectation for declared work. FocusStateDriving FocusState = "driving" )
type FocusTarget ¶ added in v0.5.0
type FocusTarget struct {
Target *Entry
ResolvedActors []string // per-involvement override or focus default; empty = pull-available
ActorsExplicit bool // actors came from the involvement triple, not the focus default
ResolvedWhen *FocusWhen // per-involvement override or focus default
Score float64 // heat under the section's rank (0 if unranked)
State FocusState
}
FocusTarget is one (focus, involvement-triple) row with the target entry resolved and per-row attributes computed. Score carries the rank-time heat (when computed) so the renderer can render `{score: X.XXX}` consistently with the as-list scored output. State is derived from Score and Actors per FocusState semantics.
type FocusWhen ¶ added in v0.5.0
FocusWhen is the temporal scope for a focus or one of its involvement triples. At least one of From or To must be set when the field is present; the absent end means "open-ended in that direction." Dates are ISO YYYY-MM-DD format on disk and parsed into time.Time at validation time.
type Function ¶ added in v0.5.0
type Function struct {
Name string
Args []FunctionArg
}
Function is one step in a Section's pipeline — a name plus optional positional arguments captured in source order.
type FunctionArg ¶ added in v0.5.0
FunctionArg is one positional argument to a Function. Inspect Kind to dispatch — exactly one of Func / Number / String is meaningful.
type Graph ¶
type Graph struct {
Entries []*Entry
ByID map[string]*Entry
RefsTo map[string][]string // reverse index: entry ID -> IDs that reference it
ClosedBy map[string][]string // reverse index: entry ID -> IDs that close it
SupersededBy map[string][]string // reverse index: entry ID -> IDs that supersede it
// contains filtered or unexported fields
}
Graph holds all entries and their reference indexes.
func (*Graph) ActiveActorHeads ¶ added in v0.3.0
ActiveActorHeads returns the head actor signal of every active actor-identity chain — the head must not be closed. Ordered by head ID.
func (*Graph) ActiveFocuses ¶ added in v0.5.0
ActiveFocuses returns kind: focus decisions that are not closed and not superseded. Ordered by entry time, oldest first.
func (*Graph) ActiveRoles ¶ added in v0.3.0
ActiveRoles returns all kind: role decisions that are derived-active by the cascade (DerivedStatus == StatusActive). Ordered by bound actor canonical then role entry ID so presenters can group without re-sorting.
func (*Graph) Activities ¶ added in v0.2.0
Activities returns active activity decisions (not closed, not superseded). Activities are THAT-shaped commitments — capturing that specific work happens, independent of the directive-style choice of *what* to do.
func (*Graph) ActorChains ¶ added in v0.3.0
func (g *Graph) ActorChains() []*ActorChain
ActorChains groups all kind: actor signals into supersession chains. Each chain surfaces its head, full entry list, and canonical history. Pure computation — no I/O. Returned in deterministic order by head ID.
func (*Graph) Actors ¶ added in v0.3.0
Actors returns all kind: actor signals regardless of chain status. Used by list/filter paths.
func (*Graph) AllParticipants ¶ added in v0.3.0
AllParticipants returns the sorted unique set of participant names that appear on any entry in the graph. Empty strings are excluded. Used by the pre-flight participant-drift check to flag names that don't match any established spelling — the caller compares each proposed name against this set.
func (*Graph) Annotations ¶ added in v0.5.0
Annotations returns all kind: annotation signals regardless of derived status. Used by list/filter paths and by topic-membership lookup.
func (*Graph) Aspirations ¶ added in v0.2.0
Aspirations returns active aspiration decisions (not superseded, not closed). Like contracts, aspirations are durable — they retire via supersede or close-by-directive with rationale (see dissolution/retirement calibration).
func (*Graph) BuildShowTree ¶
func (g *Graph) BuildShowTree(id string, upDepth, downDepth int, rendered, primaries map[string]bool) *ShowTree
BuildShowTree constructs the upstream and downstream traversal trees for a primary entry, each to its own depth, respecting cross-group dedup (rendered) and future-primary dedup (primaries). A depth of 0 skips that direction entirely. Both directions use per-direction visited sets. The rendered map is updated with newly-shown entries.
func (*Graph) ChainForCanonical ¶ added in v0.3.0
func (g *Graph) ChainForCanonical(canonical string) *ActorChain
ChainForCanonical returns the actor-identity chain that has ever held the given canonical. Returns nil when no chain matches. The write-once-across- chains invariant guarantees at most one match in a well-formed graph; callers that care about violations should use ChainsForCanonical.
func (*Graph) ChainsForCanonical ¶ added in v0.3.0
func (g *Graph) ChainsForCanonical(canonical string) []*ActorChain
ChainsForCanonical returns every actor-identity chain that has ever held the given canonical. In a well-formed graph this is zero or one; multiple results indicate a violation of the write-once-across-chains invariant and are surfaced by lint.
func (*Graph) Contracts ¶
Contracts returns active contract decisions (not superseded, not closed). Contracts retire via a same-kind supersede or a directive-kind decision closing them with rationale (universal retirement rule).
func (*Graph) DerivedStatus ¶
DerivedStatus returns the computed lifecycle status for an entry, derived from graph relationships. Superseded is checked before closed so a superseded-then-closed entry (rare) surfaces as superseded. When an entry has multiple closers/superseders, or its closer/superseder is itself superseded, the By is resolved to the live head of that chain (via ResolveRef) — never a stale intermediate. This keeps the reported closer active: a superseded closer resolves forward to the active entry that replaced it, so an entry re-closed under done-supersession reports the live done rather than the retired one. Open/closed membership is unaffected (closedSet/supersededSet) — only the reported attribution.
Role decisions additionally derive status via the actor-chain cascade (see ActorChain / RoleStatus in actor.go). A role whose bound actor chain has a closed head surfaces as StatusCascadeClosedBy. A role whose Actor does not match any chain's canonical history surfaces as StatusCascadeOrphan — an abnormal state flagged by lint.
func (*Graph) Directives ¶ added in v0.2.0
Directives returns active directive decisions (not closed, not superseded). Allow-list shape keeps future decision kinds from silently flooding this set — each kind gets surfaced deliberately with its own accessor.
func (*Graph) Downstream ¶
Downstream returns entries that reference, close, or supersede the given ID. Results are sorted by time (oldest first).
func (*Graph) EffectiveTopics ¶ added in v0.5.0
EffectiveTopics returns the merged topic-path set for an entry: inline `topics:` declared on the entry's own frontmatter unioned with topics declared by every kind: annotation entry whose refs (or per-topic members sub-selection) include this entry. Deduplicated case-insensitively with first-seen casing winning. Used by the topic(L) filter and by display rendering. Pure — uses the graph's reverse-ref index for annotation lookups, no I/O.
func (*Graph) Filter ¶
func (g *Graph) Filter(f GraphFilter) []*Entry
Filter returns entries matching the given filter criteria. Zero-value fields match all. Kind matches across both signal and decision kinds — the two sets are disjoint, so `Kind: KindGap` selects signals and `Kind: KindPlan` selects decisions without further narrowing.
func (*Graph) Focuses ¶ added in v0.5.0
Focuses returns all kind: focus decisions regardless of derived status. Used by list/filter paths.
func (*Graph) OpenSignals ¶
OpenSignals returns signals that are closure-gated attention items — gaps awaiting a decision/done and questions awaiting dissolution. Facts, insights, and done signals are deliberately excluded: facts and insights are stable observational records (retired via directive close, not resolved), and done signals are terminal facts of execution. The allow-list shape means new signal kinds default to "not an attention item" rather than silently flooding the open set.
func (*Graph) RecentDone ¶ added in v0.2.0
RecentDone returns the last n kind: done signals by timestamp — the activity stream of what was recently accomplished. Replaces the pre-two-type RecentActions; actions no longer exist in the two-type model.
func (*Graph) RecentInsights ¶ added in v0.2.0
RecentInsights returns the last n kind: insight signals by timestamp — observational records that inform current thinking. Insights have no closure gate (they're retired via directive-close, not resolved), so they surface as their own stream rather than mixing into the actionable Open Signals view.
func (*Graph) RefChain ¶
RefChain returns the entry and all entries it transitively references, in dependency order.
func (*Graph) ResolveID ¶
ResolveID resolves a user-supplied ID string (full or short form) to a full entry ID. Full IDs pass through unchanged. Short form {type}-{layer}-{suffix} is matched against entries; a unique match returns the full ID, ambiguous matches return an error listing all candidates (sorted). Inputs that do not recognize as either shape, that use unknown type or layer abbreviations, or that match zero entries pass through unchanged so the caller's existing "entry not found" surface fires against the user's original text.
func (*Graph) ResolveIDs ¶
ResolveIDs resolves a slice of user-supplied IDs. Ambiguous inputs stop resolution and return the error; other inputs pass through the same semantics as ResolveID.
func (*Graph) ResolveRef ¶ added in v0.9.0
func (g *Graph) ResolveRef(id string) ResolvedRef
ResolveRef walks the supersession chain from the given target ID to its live head and returns the ordered path (origin first, head last). A target that is not superseded resolves to a single-element path (Head == Origin). This is the general read-time generalization of the actor-identity head-walk (ActorChain.Head): any reference into a multiply-superseded chain resolves to the current entry rather than leaving the reader to hop link by link.
Forks (an entry with more than one superseder) are an anomaly flagged by lint; the walk follows the first superseder at each hop and guards against cycles, so it always terminates. Distinct from ResolveRefIDs, which resolves short-form ID strings to full form — this resolves a full ID forward through supersession.
func (*Graph) ResolveRefIDs ¶ added in v0.6.0
ResolveRefIDs resolves the ID component of each Ref against the graph, preserving Kind and Desc. Used by the new-entry handler so refs flow through the same short-form-to-full-form resolution as bare-ID fields.
func (*Graph) ResolveRoleChain ¶ added in v0.3.0
func (g *Graph) ResolveRoleChain(role *Entry) *ActorChain
ResolveRoleChain returns the actor-identity chain a role binds to via the role's Actor canonical. Returns nil when the role is not kind: role, has no Actor, or the canonical resolves to no chain (orphan — surfaced by lint). Used by both DerivedStatus and finders that need to render the resolved chain alongside the role.
func (*Graph) Roles ¶ added in v0.3.0
Roles returns all kind: role decisions regardless of derived status. Used by list/filter paths that render all roles (including retired).
func (*Graph) SetGraphDir ¶
SetGraphDir records the directory the graph was loaded from. Used by IO callers (e.g. sdd.LoadGraph) to attach provenance after constructing the in-memory graph.
func (*Graph) TopicCounts ¶ added in v0.7.0
TopicCounts buckets the given entries by their effective topic labels and returns one CountRow per distinct topic: the number of entries carrying it and their summed heat (default exp-14d decay, the same default top(N) ranks by). Buckets are keyed case-insensitively (FoldKey) with first-seen casing winning for display, matching topic-path comparison semantics elsewhere.
Rows are ordered count descending, then heat descending, then label ascending — the busiest clusters first. Entries with no effective topics contribute to no row; the `untagged` filter surfaces those separately.
Pure: reads the graph's reverse-ref index via EffectiveTopics and the heat scorer, no I/O. now is injected so callers can pin the clock for tests.
func (*Graph) TopologicalOrder ¶
TopologicalOrder returns entries sorted so that every entry appears after all entries it references (refs, closes, supersedes). Entries with no references come first. This is a stable sort — entries at the same depth are ordered by time.
type GraphFilter ¶
type GraphFilter struct {
Type EntryType
Layer Layer
Kind Kind
MissingKind bool // when true, only include entries whose stored kind field is empty
OpenOnly bool // when true, exclude closed/superseded signals and decisions
}
GraphFilter specifies criteria for filtering graph entries.
type Group ¶ added in v0.5.0
Group is one bucket inside a Grouped result. Key is the field value shared by every entry — e.g. for `group(by(kind))` over a decisions filter, Key would be "plan", "directive", and so on. Entries preserve the order they appeared in the pre-group input (no per-group ranking in slice 5; per-group sort is reserved for a later slice).
type Grouped ¶ added in v0.5.0
type Grouped struct {
// Field names which entry attribute drove the grouping. Carried
// through to renderers in case the section header benefits from it
// (e.g. "Grouped by kind"); slice 5 doesn't render it but the value
// is preserved end-to-end for slice 6's `name(...)` modifier.
Field string
Groups []Group
}
Grouped is the result shape produced by `group(by(<field>))`. Groups are ordered for stable rendering — slice 5 sorts alphabetically by Key, which is predictable but layer-blind. The `decisions`/`signals` macros in slice 6 can introduce field-aware ordering (e.g. plan, directive, activity, contract, aspiration) without changing this shape.
func (Grouped) Shape ¶ added in v0.5.0
func (Grouped) Shape() RenderShape
Shape implements SectionData.
type IDParts ¶
type IDParts struct {
Timestamp string
Time time.Time
TypeCode string // abbreviation: "s" or "d"
LayerCode string // abbreviation: "stg", "cpt", "tac", "ops", "prc"
Suffix string
}
IDParts holds the parsed components of a document ID.
type Involvement ¶ added in v0.5.0
type Involvement struct {
Target string
// Actors carries canonical-only names. Distinguishing "unset" (inherit
// focus-level default) from "explicit empty" (deliberately
// pull-available) requires the ActorsSet field; YAML's natural decoding
// merges both into a nil slice, so we capture the distinction at
// frontmatter parse time.
Actors []string
ActorsSet bool
When *FocusWhen
}
Involvement is one entry in a kind: focus decision's involvement: list. Required: Target (entry ID this involvement is about). Optional: Actors (canonical-only; per-involvement override of focus-level default — explicit empty list means "deliberately unattributed / pull-available", distinct from the unset case which inherits the focus-level default). Optional: When (per-involvement temporal scope override).
type Kind ¶
type Kind string
Kind is a sub-type classifier carried on signals and decisions. Its allowed values depend on the entry's Type:
- Signal kinds: gap (default), fact, question, insight, done, actor, annotation
- Decision kinds: directive (default), activity, plan, contract, aspiration, role, focus
Empty Kind on a new entry is replaced by the type's default during capture.
const ( // Signal kinds. KindGap Kind = "gap" KindFact Kind = "fact" KindQuestion Kind = "question" KindInsight Kind = "insight" KindDone Kind = "done" KindActor Kind = "actor" KindAnnotation Kind = "annotation" // Decision kinds. KindDirective Kind = "directive" KindActivity Kind = "activity" KindPlan Kind = "plan" KindContract Kind = "contract" KindAspiration Kind = "aspiration" KindRole Kind = "role" KindFocus Kind = "focus" )
func DefaultKindForType ¶ added in v0.2.0
DefaultKindForType returns the kind applied when a new entry of type t is captured without an explicit --kind. Signals default to gap; decisions to directive. Other types have no default (empty).
type LLMConfig ¶ added in v0.2.0
type LLMConfig struct {
// Provider selects the runner implementation: "claude-cli" (default, uses
// the logged-in Claude Code session) or a gollm-supported provider name
// such as "anthropic", "openai", "ollama".
Provider string `yaml:"provider,omitempty"`
// Model is the provider-specific model identifier.
Model string `yaml:"model,omitempty"`
// Timeout is a Go duration string (e.g. "2m") applied per LLM call.
Timeout string `yaml:"timeout,omitempty"`
// Concurrency bounds the worker pool for batch operations. Zero means
// "use DefaultLLMConcurrency".
Concurrency int `yaml:"concurrency,omitempty"`
// OllamaEndpoint overrides the default Ollama URL for the gollm adapter.
OllamaEndpoint string `yaml:"ollama_endpoint,omitempty"`
// APIKeys maps provider name to API key. Typically lives in
// config.local.yaml so keys stay out of version control.
APIKeys map[string]string `yaml:"api_keys,omitempty"`
// RateLimitRPS caps remote-provider requests per second. Zero means
// "apply a conservative per-model default safe for Anthropic/OpenAI
// tier 1"; set an explicit positive value (e.g. a high number like
// 100) to effectively disable the cap on higher tiers. The claude-cli
// and ollama providers ignore this field.
RateLimitRPS float64 `yaml:"rate_limit_rps,omitempty"`
// Retry tunes the provider-aware retry policy applied by the gollm
// runner (transient-error classification, server-provided backoff,
// exponential backoff with jitter). Ignored by the claude-cli provider,
// whose own CLI handles retries.
Retry RetryConfig `yaml:"retry,omitempty"`
}
LLMConfig holds settings for LLM provider selection, model choice, and concurrency/rate-limit behavior. API keys and per-machine endpoints typically live in .sdd/config.local.yaml; defaults (provider, model, timeout, concurrency) are safe to commit in .sdd/config.yaml.
type Layout ¶ added in v0.5.0
type Layout struct {
Sections []Section
}
Layout is the parsed root of a `--layout=...` argument to `sdd view`. Each Section corresponds to one comma-separated entry in the source string; sections are rendered in source order.
type ModelRollup ¶ added in v0.9.0
type ModelRollup struct {
Model string
Provider string
StatMetrics
}
ModelRollup aggregates calls for one (model, provider) pair.
type OpRollup ¶ added in v0.9.0
type OpRollup struct {
Op string
StatMetrics
}
OpRollup aggregates calls for one operation.
type ParticipantsBlock ¶ added in v0.5.0
type ParticipantsBlock struct {
Groups []ParticipantsGroup
}
ParticipantsBlock is the as-participants-block-bound SectionData variant. Groups appear in source order — the finder's actor-head walk preserves the active-actor ordering used by `sdd status`.
func (ParticipantsBlock) Shape ¶ added in v0.5.0
func (ParticipantsBlock) Shape() RenderShape
Shape implements SectionData.
type ParticipantsGroup ¶ added in v0.5.0
ParticipantsGroup couples one active actor head with the derived-active roles bound to its chain. Mirrors query.ParticipantGroup for status — kept as a separate model type so view-layer rendering doesn't depend on query types and the SectionData contract holds at the model boundary.
type Ref ¶ added in v0.6.0
Ref is one entry reference with its semantic kind and optional inline description. The object form ({id, kind, desc?}) is the canonical on-disk representation; bare-string entries (legacy) parse with Kind = RefKindUnknown so existing graphs keep working while new captures carry the metadata.
func (Ref) MarshalYAML ¶ added in v0.6.0
MarshalYAML emits the canonical object form with id, kind, desc ordering. Bare-string output is never produced — writes always use object form even when carrying a legacy ref with Kind = RefKindUnknown (round-trip case).
func (Ref) OnDiskKind ¶ added in v0.7.0
OnDiskKind returns the ref kind as stored on disk, before legacy alias resolution (grounds/evidence → grounded-in). It falls back to Kind for every ref that was not alias-resolved, including in-memory-constructed refs. Used only by summary-prompt rendering to keep summary hashes stable across the rename (s-tac-koz); pre-flight and all user-facing rendering use Kind.
func (*Ref) UnmarshalYAML ¶ added in v0.6.0
UnmarshalYAML accepts either a scalar string (legacy form; Kind defaults to RefKindUnknown) or a mapping with `id`, `kind`, and optional `desc` keys. Object form requires both `id` and `kind`; an invalid kind value fails to parse so malformed entries don't silently enter the graph.
type RefExpansion ¶ added in v0.7.0
type RefExpansion struct {
Kind RefKind
ID string
Status Status
Desc string
SupersedePath []string
}
RefExpansion is one resolved outgoing reference for expand(refs) output. The finder resolves each row from the loaded graph: Kind is the ref's stored semantic kind (the presenter renders it as the sub-line verb), ID is the referenced entry's full ID, Status is that entry's derived status, and Desc is the optional per-ref rationale. A ref whose target is missing from the graph (dangling — lint surfaces it elsewhere) carries a zero Status, which renders without a status segment.
SupersedePath is populated only when the target is superseded: the ordered supersession trail from the target (origin) to its live head (ResolveRef Path). The presenter renders the hops through to the head so a reader can connect a stale intermediate ID met elsewhere; it stays nil otherwise.
type RefKind ¶ added in v0.6.0
type RefKind string
RefKind classifies the semantic relationship a reference describes — why the contextual pointer exists, chosen from what the entry body asserts. The closed vocabulary is defined by principle (the relationship and its direction), not by which target types a kind is allowed on. It is enforced at capture (pre-flight uses IsCapturableRefKind). Legacy entries with bare-string refs parse as RefKindUnknown for traversal compatibility but cannot be authored anew; legacy alias kinds (grounds, evidence) resolve to their canonical replacement at parse time (see refKindAliases).
const ( RefKindGroundedIn RefKind = "grounded-in" // founded on / reasons from the target — basis, premise, or empirical proof (absorbs legacy evidence) RefKindBuildsOn RefKind = "builds-on" // continues / extends a closed prior line of work RefKindRefines RefKind = "refines" // sharpens, narrows, or clarifies an active target in place — the augmenting-directive pattern (d-prc-9ti) RefKindAddresses RefKind = "addresses" // acts on / realizes the target — responds to a signal or fulfils a decision's commitment RefKindSurfaces RefKind = "surfaces" // forward class: created or discovered the target during this work RefKindDependsOn RefKind = "depends-on" // functional prerequisite — source needs target first RefKindRequiredBy RefKind = "required-by" // forward class: inverse of depends-on — target needs source first RefKindRelated RefKind = "related" // parallel sibling / contextual neighbor — the floor, never a default // Legacy alias kinds — never authored anew. Resolved to their canonical // kind at parse time (refKindAliases) so nothing above the parser sees them, // and absent from the capturable set so new captures are rejected. On-disk // entries keep their original value; history is never rewritten. RefKindGrounds RefKind = "grounds" // → grounded-in (renamed) RefKindEvidence RefKind = "evidence" // → grounded-in (merged; empirical hardness reads from the target's kind) RefKindUnknown RefKind = "unknown" // legacy bare-string fallback — read-only, not authored )
func AdmissibleRefKinds ¶ added in v0.10.0
func AdmissibleRefKinds(class RefTargetClass) []RefKind
AdmissibleRefKinds returns the capturable kinds whose precondition holds for the target class, in display order. This is the set rendered into the pre-flight prompt per ref and the set a mechanical inapplicability finding names as alternatives.
func RefKindValues ¶ added in v0.6.0
func RefKindValues() []RefKind
RefKindValues returns the capturable kind vocabulary in display order. Used to render the closed set in error messages and documentation.
type RefKindCell ¶ added in v0.10.0
RefKindCell is one matrix cell: whether the kind's precondition holds for the target class, plus the nuance note. For applicable cells the note carries the reading under which the kind fits (rendered into the pre-flight prompt); for inapplicable cells it names the violated precondition and the admissible alternatives (rendered into the mechanical finding).
func RefKindApplicability ¶ added in v0.10.0
func RefKindApplicability(kind RefKind, class RefTargetClass) (RefKindCell, bool)
RefKindApplicability returns the matrix cell for a capturable kind against a target class. The second return is false for kinds outside the capturable vocabulary (unknown, legacy aliases) — those are rejected by the separate capturable-kind mechanical check, not classified here.
type RefTargetClass ¶ added in v0.10.0
type RefTargetClass string
RefTargetClass reduces a ref target's kind and derived status to the axes the applicability matrix keys on. Status is classified before kind so a retired entry (closed, superseded, cascade-closed, orphan) is TargetRetired regardless of its kind — including the rare corrective-done-closed done.
const ( TargetLiveDecision RefTargetClass = "live-decision" // active decision (any decision kind) TargetLiveSignal RefTargetClass = "live-signal" // open signal (gap, fact, question, insight, actor, annotation) TargetTerminalDone RefTargetClass = "terminal-done" // done signal — terminal fact of execution, no lifecycle TargetRetired RefTargetClass = "retired" // closed, superseded, cascade-closed, or orphan )
func ClassifyRefTarget ¶ added in v0.10.0
func ClassifyRefTarget(target *Entry, status Status) RefTargetClass
ClassifyRefTarget maps a target entry plus its derived status to the matrix axis. Callers pass the status from Graph.DerivedStatus so classification and rendering agree on liveness.
type RenderShape ¶ added in v0.5.0
type RenderShape string
RenderShape is the data-shape contract between finders (which produce per-section results) and presenters (which render them). Each concrete SectionData variant declares the shape it carries so a presenter can validate before dispatching — e.g. as-grouped expects a grouped shape; receiving a flat-list is a render-shape mismatch.
const ShapeCounts RenderShape = "counts"
ShapeCounts marks results carrying per-topic aggregate rows — produced by a section ending in `as-counts`. Each row is one effective-topic label with the number of entries carrying it and their summed heat. Mutually exclusive with the other shapes: a section produces one, never several.
const ( // ShapeFlatList is an ordered sequence of entries — consumed by as-list. ShapeFlatList RenderShape = "flat-list" )
const ShapeFocusBlock RenderShape = "focus-block"
ShapeFocusBlock is the result shape produced by `expand(involvement)` over a list of focus entries, terminated by `as-focus-block`. It carries one entry per active focus, each with its involvement targets resolved to graph entries and tagged with derived state.
const ShapeGrouped RenderShape = "grouped"
ShapeGrouped marks results carrying named buckets of entries — produced by a section ending in `as-grouped` after a `group(by(<field>))` aggregation. The grouped shape is mutually exclusive with the flat shape (FlatList): a section produces one or the other, never both.
const ShapeParticipantsBlock RenderShape = "participants-block"
ShapeParticipantsBlock is the result shape produced by sourcing actor entries terminated by `as-participants-block`. Mirrors the Participants section in `sdd status` (one group per active actor canonical, with derived-active roles bound to that chain) so view-side rendering reuses the same visual contract.
const ShapeWipList RenderShape = "wip-list"
ShapeWipList is the result shape produced by `source(wip)` terminated by `as-wip-list`. Carries the active WIP markers in the order they were loaded — chronological by ID per Finder.LoadWIPMarkers.
type ResolvedRef ¶ added in v0.9.0
type ResolvedRef struct {
// contains filtered or unexported fields
}
ResolvedRef is the resolution of a target ID to its live head by walking supersession transitively. The path runs origin-first to head-last; a target that nothing supersedes resolves to a single-element path whose head is the origin itself. path is unexported so callers cannot index an intermediate and mistake a superseded link for the live head — Head() is the accessor every reasoning consumer should reach for, with the stale trail available via Path() for rendering only.
func (ResolvedRef) Head ¶ added in v0.9.0
func (r ResolvedRef) Head() string
Head returns the live head of the supersession chain — the entry the reference resolves to now. For a non-superseded target this is the target itself.
func (ResolvedRef) IsStale ¶ added in v0.9.0
func (r ResolvedRef) IsStale() bool
IsStale reports whether the origin was superseded — the reference points at an entry that has since been replaced, so the head differs from the origin.
func (ResolvedRef) Origin ¶ added in v0.9.0
func (r ResolvedRef) Origin() string
Origin returns the ID the resolution started from — the literal target of the reference, which may be a superseded (stale) entry.
func (ResolvedRef) Path ¶ added in v0.9.0
func (r ResolvedRef) Path() []string
Path returns the ordered supersession trail from origin to head (origin first, head last). Rendering-only — reasoning consumers want Head(). The returned slice is a copy; mutating it does not affect the resolution.
type RetryConfig ¶ added in v0.7.0
type RetryConfig struct {
// MaxAttempts is the total number of attempts per call (1 = no retry).
// Zero means DefaultRetryMaxAttempts.
MaxAttempts int `yaml:"max_attempts,omitempty"`
// BaseDelay is the exponential-backoff floor as a Go duration string
// (e.g. "2s"). Empty means DefaultRetryBaseDelay.
BaseDelay string `yaml:"base_delay,omitempty"`
// MaxDelay caps backoff growth as a Go duration string (e.g. "60s").
// Empty means DefaultRetryMaxDelay.
MaxDelay string `yaml:"max_delay,omitempty"`
}
RetryConfig tunes retry/backoff behavior for the gollm-backed runner. Each zero-valued field falls back to the corresponding DefaultRetry* constant via Resolved, so an absent retry block yields the baked defaults.
func (RetryConfig) Resolved ¶ added in v0.7.0
func (rc RetryConfig) Resolved() (maxAttempts int, baseDelay, maxDelay time.Duration, err error)
Resolved applies the baked defaults to any zero-valued field and parses the duration strings. An invalid duration returns an error so misconfiguration surfaces when the runner is constructed rather than silently degrading.
type SchemaMeta ¶ added in v0.2.0
type SchemaMeta struct {
GraphSchemaVersion int `json:"graph_schema_version"`
MinimumVersion *string `json:"minimum_version,omitempty"`
}
SchemaMeta is the decoded contents of .sdd/meta.json.
GraphSchemaVersion is required and advances through explicit migrations. MinimumVersion is written once at initial init (set to the binary's semver at creation time) and preserved thereafter; a nil pointer means the field is absent on disk.
func ParseSchemaMeta ¶ added in v0.2.0
func ParseSchemaMeta(data []byte) (*SchemaMeta, error)
ParseSchemaMeta decodes .sdd/meta.json bytes into a SchemaMeta.
type Scope ¶ added in v0.2.0
type Scope string
Scope selects where skills are installed for a given agent. User scope is shared across all projects for a given OS user; Project scope lives beside the repository.
type Section ¶ added in v0.5.0
type Section struct {
Functions []Function
}
Section is one colon-chained pipeline within a Layout. Functions execute left-to-right: filters and transforms accumulate, the section terminates in a render function (e.g. as-list).
type SectionData ¶ added in v0.5.0
type SectionData interface {
Shape() RenderShape
}
SectionData is one section's typed result. Variants implement Shape() to declare their render-side contract. Slice 1 has only FlatList; later slices add Grouped, FocusBlock, etc.
type ShowTree ¶
type ShowTree struct {
Primary *Entry
PrimaryStatus Status
PrimarySupersedePath []string
PrimaryTopics []TopicPath
Upstream []ShowTreeItem
Downstream []ShowTreeItem
}
ShowTree holds the upstream and downstream chains for a single primary entry. Primary-derived attributes (status, supersede trail, effective topics) are computed during the build so presenters consume precomputed values and stay pure — no graph traversal at render time.
type ShowTreeItem ¶
type ShowTreeItem struct {
Entry *Entry
Depth int
Relations []string // e.g. ["refs"], ["refs", "closes"], ["refd-by"]
RefKind RefKind // kind of the refs edge linking parent to this entry (empty when no refs relation or no kind metadata)
RefDesc string // desc of the refs edge (when present)
ShownAbove bool // already rendered earlier — "(see above)" marker
ShownBelow bool // future primary — "(see below)" marker
SummaryOnly bool // true for depth > 0
Truncated []TruncatedRef // children hidden at depth boundary
Status Status // derived lifecycle status, computed at build so renderers stay pure
SupersedePath []string // resolved origin→head trail when superseded; nil otherwise
}
ShowTreeItem represents an entry at a specific depth in a show tree. Depth 0 is the primary entry. Depth 1+ entries are summary-only.
type SkillBundle ¶ added in v0.2.0
type SkillBundle struct {
Target AgentTarget
Entries []SkillBundleEntry
}
SkillBundle is the set of files embedded in the binary for a single agent target.
type SkillBundleEntry ¶ added in v0.2.0
type SkillBundleEntry struct {
// Skill is the top-level skill directory (e.g. "sdd", "sdd-explore").
Skill string
// RelPath is the path inside the skill directory (e.g. "SKILL.md",
// "references/framework-concepts.md"). Forward-slash separators.
RelPath string
// Content is the raw file bytes as embedded.
Content []byte
}
SkillBundleEntry is one file in the embedded skill bundle — the content as shipped inside the sdd binary, without install-time stamps.
func ResolveSkillIncludes ¶ added in v0.7.0
func ResolveSkillIncludes(entries []SkillBundleEntry) ([]SkillBundleEntry, error)
ResolveSkillIncludes replaces sdd:include directives in each entry with the body of the referenced file in the same skill. It runs once at bundle load so every downstream consumer sees identical resolved content. Resolution is single-pass: an included fragment is not itself rescanned for includes. A directive whose target is absent from the bundle is an error.
type SkillFile ¶ added in v0.2.0
type SkillFile struct {
// AbsPath is the absolute path where the file lives on disk.
AbsPath string
// Content is the raw file bytes as read.
Content []byte
// StoredVersion is the value of the sdd-version frontmatter stamp, or
// empty if absent.
StoredVersion string
// StoredHash is the value of the sdd-content-hash frontmatter stamp, or
// empty if absent.
StoredHash string
}
SkillFile is a parsed on-disk skill file, carrying the install stamps read from its frontmatter plus the raw content needed to re-hash.
func ParseSkillFile ¶ added in v0.2.0
ParseSkillFile reads an on-disk skill file's raw bytes and extracts its install-time stamps.
type SkillInstallStatus ¶ added in v0.2.0
type SkillInstallStatus string
SkillInstallStatus describes the install state of a single skill file relative to the embedded bundle.
const ( // SkillStatusMissing means the file has no on-disk counterpart. SkillStatusMissing SkillInstallStatus = "missing" // SkillStatusCurrent means the on-disk file's stored hash matches the // embedded entry's hash and the user has not edited it. SkillStatusCurrent SkillInstallStatus = "current" // SkillStatusPristine means the on-disk file was produced by a previous // sdd init (user hasn't edited it) but its stored version differs from // the embedded bundle — safe to overwrite silently. SkillStatusPristine SkillInstallStatus = "pristine" // SkillStatusModified means the on-disk file's computed hash does not // match its stored hash — the user has edited it, overwrite requires // confirmation. SkillStatusModified SkillInstallStatus = "modified" )
func ComputeSkillStatus ¶ added in v0.2.0
func ComputeSkillStatus(embedded SkillBundleEntry, installed *SkillFile) SkillInstallStatus
ComputeSkillStatus classifies an installed file relative to its embedded counterpart. When installed is nil, the file is missing.
An unstamped installed file (empty StoredHash) is treated as Pristine when its content byte-matches the embedded entry — first-run adoption of a bundled skill shouldn't reflexively prompt the user about files they haven't touched. Unstamped files whose content doesn't match are Modified.
type StatMetrics ¶ added in v0.9.0
type StatMetrics struct {
Calls int
Items int
InputTokens int
OutputTokens int
CacheReadTokens int
CacheCreateTokens int
DurationMS int64
}
StatMetrics holds the summed counters for a group of calls plus the derived throughput math. Embedded into the per-model and per-op rollups and reused for the report totals so the accumulation and throughput logic live once.
func (StatMetrics) HasItems ¶ added in v0.9.0
func (m StatMetrics) HasItems() bool
HasItems reports whether this group carries item counts (the embedding ops). Chat ops (preflight, summarize) leave Items zero, so items/s is meaningless for them and renders blank.
func (StatMetrics) ItemsPerSec ¶ added in v0.9.0
func (m StatMetrics) ItemsPerSec() float64
ItemsPerSec is summed items over summed wall-clock seconds (embedding ops). Zero when no items or no time were recorded; pair with HasItems to decide whether to render it.
func (StatMetrics) MsPerCall ¶ added in v0.9.0
func (m StatMetrics) MsPerCall() float64
MsPerCall is the mean wall-clock duration per call in milliseconds.
func (StatMetrics) TokensPerSec ¶ added in v0.9.0
func (m StatMetrics) TokensPerSec() float64
TokensPerSec is summed input tokens over summed wall-clock seconds. Zero when no time was recorded.
type StatsRecord ¶ added in v0.9.0
type StatsRecord struct {
Timestamp time.Time
Op string
Provider string
Model string
Items int
InputTokens int
OutputTokens int
CacheReadTokens int
CacheCreateTokens int
DurationMS int64
}
StatsRecord is one LLM/embedding call's metrics in domain form — the parsed shape the aggregation works over. It mirrors the on-disk sink record but carries a parsed timestamp and no JSON concerns (those live at the I/O boundary in internal/llmstats).
func FilterStats ¶ added in v0.9.0
func FilterStats(records []StatsRecord, since *time.Time, op, provider, model string) []StatsRecord
FilterStats returns the records matching the given range and exact-match filters. A nil since means all-time; empty op/provider/model strings mean no constraint on that field. The input slice is not mutated.
type StatsReport ¶ added in v0.9.0
type StatsReport struct {
Totals StatMetrics
ByModel []ModelRollup
ByOp []OpRollup
}
StatsReport is the aggregated view of a filtered record set: overall totals plus per-model and per-op rollups, each sorted call-count-descending then by name for deterministic output.
func AggregateStats ¶ added in v0.9.0
func AggregateStats(records []StatsRecord) StatsReport
AggregateStats rolls records up into totals, by-model, and by-op groups.
type Status ¶
type Status struct {
Kind StatusKind
By string
}
Status is the computed lifecycle status for an entry. By is populated only for compound states (ClosedBy, SupersededBy) and holds the full entry ID of the live head closer/superseder (see DerivedStatus).
type StatusKind ¶
type StatusKind string
StatusKind is the lifecycle state of an entry derived from graph relationships.
const ( StatusNone StatusKind = "" // done signals — terminal facts with no lifecycle state StatusActive StatusKind = "active" // decision (directive, plan, contract) not closed or superseded StatusOpen StatusKind = "open" // signal not closed or superseded StatusClosedBy StatusKind = "closed-by" // closed by another entry (By carries the full ID) StatusSupersededBy StatusKind = "superseded-by" // superseded by another entry StatusCascadeClosedBy StatusKind = "cascade-closed" // role cascade: actor chain head is closed (By = head actor ID) StatusCascadeOrphan StatusKind = "cascade-orphan" // role references a canonical with no matching chain )
type SyncConfig ¶ added in v0.3.0
type SyncConfig struct {
// Cooldown is the minimum interval between background git fetches. Go
// duration string (e.g. "15m", "1h"). Empty means DefaultSyncCooldown.
Cooldown string `yaml:"cooldown,omitempty"`
}
SyncConfig governs background sync awareness: the auto-fetch cooldown and related behavior. Stored as a string Go duration (e.g. "15m") parsed at use site so malformed values fall back to DefaultSyncCooldown rather than failing at config load.
type SyncState ¶ added in v0.3.0
type SyncState string
SyncState describes the overall state of background sync detection.
const ( // SyncStateUpToDate: no divergence between local and remote. SyncStateUpToDate SyncState = "up-to-date" // SyncStateFastForward: remote has commits local does not; local has none // ahead. A rebase is a fast-forward with no conflict risk. SyncStateFastForward SyncState = "fast-forward" // SyncStateCleanRebase: both sides have commits, but git merge-tree // predicts the rebase would apply cleanly. SyncStateCleanRebase SyncState = "clean-rebase" // SyncStateConflictPredicted: both sides have diverged and the // git merge-tree prediction reports conflicts. Rebase is unsafe without // manual resolution. SyncStateConflictPredicted SyncState = "conflict-predicted" // SyncStateLocalAhead: local has commits remote does not, and remote has // none ahead of local. Push is suggested. SyncStateLocalAhead SyncState = "local-ahead" // SyncStateNoRepo: current directory is not inside a git repository. SyncStateNoRepo SyncState = "no-repo" // SyncStateNoRemote: repository has no remote configured. SyncStateNoRemote SyncState = "no-remote" // SyncStateNoUpstream: current branch has no upstream tracking branch. SyncStateNoUpstream SyncState = "no-upstream" // SyncStateFetchFailed: git fetch was attempted and failed (network, // authentication, etc.). The last-fetch timestamp is still updated so // subsequent commands within the cooldown window do not retry. SyncStateFetchFailed SyncState = "fetch-failed" // SyncStateSkipped: the sync check did not run (cooldown window active, // command exempt, or environment unavailable). No slog output expected. SyncStateSkipped SyncState = "skipped" )
type SyncStatus ¶ added in v0.3.0
type SyncStatus struct {
State SyncState
RemoteAhead int // count of remote-ahead commits matching ^sdd:
LocalAhead int // count of local-ahead commits matching ^sdd:
ConflictPaths []string // populated when State == SyncStateConflictPredicted
// Reason carries a short human-readable detail for failure states —
// the upstream branch name for NoUpstream, the error message for
// FetchFailed, etc. Empty for success states.
Reason string
}
SyncStatus is the structured result of a background sync check. Callers emit the appropriate slog line based on State; the skill pattern-matches on the rendered message.
type TopicPath ¶ added in v0.5.0
type TopicPath struct {
// Components are the path elements in order, preserving original casing.
// Length is always >= 1 for a valid TopicPath; the zero value is invalid.
Components []string
}
TopicPath is a hierarchical topic label expressed as path components. Internal representation is []string of components (e.g. "infrastructure/cli" is []string{"infrastructure", "cli"}); I/O representation joins components with "/". Comparison is case-insensitive on each component, but the original casing is preserved on the value so first-seen casing can win as canonical for display.
Component validation: each component matches [\p{L}\p{N}\-]+ (Unicode letter or number plus hyphen). Empty components — leading, trailing, or consecutive "/" — are rejected.
func CanonicalizeTopicPaths ¶ added in v0.5.0
CanonicalizeTopicPaths walks paths in order and returns a deduplicated slice where the first-seen casing of each fold-key wins. Used by display rendering and by graph traversal merging inline topics with annotation memberships.
func ParseTopicPath ¶ added in v0.5.0
ParseTopicPath parses a "/"-joined topic path string into a TopicPath. Empty input, leading/trailing "/", consecutive "//", or components that violate the component-character rule return an error.
func (TopicPath) Equal ¶ added in v0.5.0
Equal reports whether two paths match component-wise, case-insensitively. Length must be identical; comparison is per-component.
func (TopicPath) FoldKey ¶ added in v0.5.0
FoldKey returns a comparable lower-case key for use as a map key when deduplicating paths case-insensitively. The original casing is lost in the key but preserved on the source TopicPath value.
func (TopicPath) HasPrefix ¶ added in v0.5.0
HasPrefix reports whether t starts with prefix component-wise, case- insensitively. A path is a prefix of itself. Used by the topic(L) filter: `topic("UX")` matches `UX`, `UX/CLI`, `UX/CLI/Status`; does NOT match `UXTesting` (because comparison is component-wise, not raw-string).
type TruncatedRef ¶
type TruncatedRef struct {
ID string
Relations []string
Kind Kind
RefKind RefKind // kind of the refs edge into this truncated entry
RefDesc string // desc of the refs edge
}
TruncatedRef describes a child entry hidden at the depth boundary.
type WIPMarker ¶
type WIPMarker struct {
ID string // filename without .md: {YYYYMMDD}-{HHmmss}-{participant}
Entry string // graph entry ID being worked on
Participant string
Exclusive bool
Branch string // git branch name (empty if not a branched work stream)
Content string // free-text description
Time time.Time
}
WIPMarker represents an in-flight work marker.
func HasExclusiveMarker ¶
HasExclusiveMarker returns true if any marker for the given entry is exclusive.
func MarkersForEntry ¶
MarkersForEntry returns all markers that reference the given graph entry ID.
func ParseWIPMarker ¶
ParseWIPMarker parses a WIP marker from its filename and file content.
func (*WIPMarker) ShortContent ¶
ShortContent returns the first line of the marker content, truncated to maxLen.
type Warning ¶
type Warning struct {
Field string // "refs", "closes", "supersedes"
Value string // the offending ID or value
Message string // human-readable description
}
Warning represents a validation issue found on a graph entry.
type WipList ¶ added in v0.5.0
type WipList struct {
Markers []*WIPMarker
}
WipList is the as-wip-list-bound SectionData variant. Markers are referenced by pointer so the renderer can read every field without copying — markers are immutable in-memory structures shared across the section's lifetime.
func (WipList) Shape ¶ added in v0.5.0
func (WipList) Shape() RenderShape
Shape implements SectionData.