finders

package
v0.17.0 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: MIT Imports: 33 Imported by: 0

Documentation

Overview

Package finders processes domain query structs into results. Finders encapsulate the actual read logic and hold injected dependencies. Pure reads — no side effects of their own (though injected dependencies may perform IO, e.g. the LLM call behind the pre-flight runner).

Index

Constants

View Source
const DefaultStalledThreshold = 1.0

DefaultStalledThreshold is the score below which a focus target is classified as stalled when actors are assigned. Default 1.0 is "fewer than one fresh ref-equivalent post-14d-decay" — under exp-14d, a single ref two weeks old contributes 0.5, so a target needs more than two weeks of inactivity (or fewer than ~two recent refs) to drop below. Configurable via the `stalled(value)` modifier per d-tac-uww §6.

The default landed during slice 7 implementation as a starting point; the slice 8 closing findings attachment compares this default against alternatives on the live graph.

View Source
const GraphCommitGrepPattern = "^sdd:"

GraphCommitGrepPattern is the commit-message prefix emitted by every SDD handler (handler_new_entry, handler_summarize, handler_wip_start, handler_wip_done, handler_lint_fix, handler_rewrite, handler_init). The sync finder counts commits matching this pattern on either side of the local/upstream divergence to report graph activity.

Variables

This section is empty.

Functions

func BaseEntries added in v0.17.0

func BaseEntries() ([]*model.Entry, error)

BaseEntries assembles every embedded base entry shipped in the binary: the always-loaded base procedures and the base facts rendered against the live layout vocabulary. Both graph load paths (LoadGraph here, BuildSnapshot in application) merge this set with disk-wins precedence via model.MergeEmbedded, so the "what ships embedded, and with which vocabulary" wiring lives in one place. The embedded set is compile-time-shaped, so an error is a broken build.

func LiveViewVocabulary added in v0.17.0

func LiveViewVocabulary() viewlayout.Vocabulary

LiveViewVocabulary assembles the executor's live layout vocabulary from the packages that own each part of the language.

func MultiSearch added in v0.15.0

func MultiSearch(ctx context.Context, local *SearchFinder, q query.SearchQuery) (*query.SearchResult, error)

MultiSearch is the cross-graph search read: it runs the query against the local finder and against every repo the query selects, merging all hits into one list by comparable score with remote hits repo-tagged. Pure read — cache freshness and per-repo index fill are the handler's job (Handler.PrepareCrossRepoSearch), run before this like the local lazy-fill precedes a plain Search.

Members resolve internally: the member graph comes from the query graph's cross-graph assembly, the per-repo index opens read-only from the repo's cache, and every member shares the local finder's embedder — the single vector space that makes cosine scores comparable. A selected repo whose graph is unavailable is skipped with a warning (its absence is visible, not silent); a member graph that fails to load is an error.

Embedded (binary-scoped) entries surface exactly once: the local search covers them, and member hits on embedded entries are dropped (remote indexes exclude them; the guard here also covers text mode, which greps the member graph directly).

func ViewFunctionNames added in v0.17.0

func ViewFunctionNames() []string

ViewFunctionNames returns the function names accepted by the layout executor. Reference surfaces use this instead of maintaining their own vocabulary list.

func ViewRankAlgorithmNames added in v0.17.0

func ViewRankAlgorithmNames() []string

ViewRankAlgorithmNames returns the algorithms accepted by rank() in their user-facing order. Reference surfaces use the executor's registry directly.

func ViewRenderNames added in v0.17.0

func ViewRenderNames() []string

ViewRenderNames returns the render terminators accepted by the layout executor in deterministic order.

Types

type DocumentIssue added in v0.17.0

type DocumentIssue struct {
	Ref     string
	Message string
}

DocumentIssue records content a source could not decode into a document: its logical path (or ref) and the decode error message. The GraphFinder turns each into a model.LoadIssue.

type DocumentSource added in v0.17.0

type DocumentSource interface {
	GraphDocuments() (GraphDocuments, error)
}

DocumentSource is the storage-neutral seam every graph read enters through: it supplies the documents a graph is built from, regardless of where the bytes live. The filesystem source reads .md files, a structured source wraps application.SnapshotData, a future DB source queries rows — each owns its own encoding and decoding. Content a source cannot decode is reported as an unreadable document (data the read serves), never a hard failure that kills the whole load.

"Store = where bytes live (host-specific); GraphFinder = what the graph means (shared)." The DocumentSource is the store's face; the GraphFinder is the meaning.

type EntryDocument added in v0.17.0

type EntryDocument struct {
	// ID is the entry's full logical ID (no ".md"). Both forms carry it.
	ID string
	// Raw is the canonical stored content (frontmatter + body). When non-nil,
	// this form is used and the structured fields are ignored.
	Raw []byte
	// Frontmatter and Body are the structured form, used when Raw is nil.
	Frontmatter map[string]any
	Body        string
	// Attachments are graph-relative attachment paths, carried in both forms.
	Attachments []string
}

EntryDocument is the storage-neutral form of one graph entry. It is a union mirroring application.DocumentChange: it carries EITHER raw canonical content (Raw: markdown + frontmatter bytes) OR structured fields (Frontmatter + Body). Exactly one form is set. Sources own their encoding — the filesystem source hands raw content (so the CLI path does no double YAML parse and stays byte-identical to the direct loader), a structured source (SnapshotData, a future DB) hands structured fields.

type Finder

type Finder struct {
	// contains filtered or unexported fields
}

Finder holds dependencies and config shared across query methods. Config is a snapshot taken at construction time — short-lived CLI means a single read at the composition root is sufficient. Nil means the finder was composed without Options.Config — a wiring fault, never a graph state — so the config accessors error rather than degrade (s-tac-uya). A composition root where absence is a legitimate state (fresh repo, read-only commands, tests) resolves it to an explicit empty PerRepoConfig instead of passing nil.

func New

func New(opts Options) *Finder

New constructs a Finder with the given options.

func (*Finder) CurrentGraph added in v0.14.1

func (f *Finder) CurrentGraph(dir string) (*model.Graph, error)

CurrentGraph sources the current graph once — the one-shot read every short-lived consumer uses instead of calling LoadGraph directly, so all reads share the one GraphSource seam. No memo is retained across calls (each call is its own scope), so a fresh command or request always loads fresh.

func (*Finder) EffectiveConfig added in v0.15.0

EffectiveConfig resolves the config overlay layer by layer and reports each effective value with the layer that supplied it. The finder reads the layers itself (rather than using the merged construction-time snapshot) because provenance is exactly the information merging discards.

func (*Finder) IndexLint added in v0.15.0

func (f *Finder) IndexLint(q query.IndexLintQuery, result *query.LintResult)

IndexLint appends index-health findings when an embedding provider is configured. Loading the manifest and building an embedder are both pure operations against config — no graph mutation, no embedding calls (only the fingerprint is read). Errors degrade silently to "index not configured" so a missing dependency in lint doesn't block graph-side validation. Drift is advisory: the next index run or search converges it.

func (*Finder) Info added in v0.5.2

func (f *Finder) Info(_ query.InfoQuery) (*query.InfoResult, error)

Info gathers session framing for the `sdd info` command — the session header surface that skill session-start injections read. Pure config inspection — no graph access required.

func (*Finder) LoadGraph

func (f *Finder) LoadGraph(dir string) (*model.Graph, error)

LoadGraph reads all .md files from dir (hierarchical YYYY/MM/ layout), joins the base procedure entries embedded in the binary, and builds the graph. Base entries are always loaded — a project graph never contains them on disk, but its entries may supersede them (procedure customization). On the unlikely ID collision, the disk entry wins: a project owns its graph directory.

This is now a thin adapter: the filesystem DocumentSource supplies the raw documents and GraphFinder applies the single semantic gate (parse, base merge, partial-read LoadIssues). The signature is kept so CLI and handler callers are untouched; the graph's directory is recorded for provenance and lazy WIP resolution.

func (*Finder) LoadWIPMarkers

func (f *Finder) LoadWIPMarkers(graphDir string) ([]*model.WIPMarker, error)

LoadWIPMarkers reads all marker files from the wip/ subdirectory of graphDir.

func (*Finder) NewGraphSource added in v0.14.1

func (f *Finder) NewGraphSource(dir string) *GraphSource

NewGraphSource builds a memoizing source over dir. Held by long-lived readers (the engine session); one-shot readers use CurrentGraph.

The loaded graph is assembled into a model.MultiGraph here — the single insertion point for cross-repo reads. Member graphs load lazily from the connected-repos caches (pure reads; clone/pull side effects run in handlers that invalidate this source), so a graph with no cross-repo touch pays nothing.

func (*Finder) OnGraph added in v0.17.0

func (f *Finder) OnGraph(g *model.Graph) *GraphFinder

OnGraph wraps an already-built graph so the finder's read methods operate on it with intent-only queries. Used by callers that already hold a graph — the engine's *model.Graph, a MultiGraph-assembled clone, the CLI's CurrentGraph, tests — rather than building one from a document source. WIP markers are resolved lazily from the graph's directory when a view needs them; callers with markers in hand attach them via WithWIP.

func (*Finder) Preflight

Preflight validates an entry against the given graph. It is the explicit- graph entry point handlers use (they hold a graph mid-write and consume the finder through the handlers.Reader interface); it binds the graph and forwards to the GraphFinder so the validation logic stays single.

func (*Finder) SchemaStatus added in v0.2.0

SchemaStatus reads .sdd/meta.json (if present) and evaluates its compatibility fields against the calling binary's version and schema version. Pure read; no side effects.

When meta.json is absent the result reports MetaExists = false and Compatible = true — an uninitialised graph is the init command's responsibility, not the write gate's.

func (*Finder) SkillStatus added in v0.2.0

SkillStatus loads the embedded skill bundle for the query's target, then walks each entry to compare its embedded content against the file on disk. Classification happens in model.ComputeSkillStatus — the finder only supplies inputs.

func (*Finder) Stats added in v0.9.0

func (f *Finder) Stats(q query.StatsQuery) (*query.StatsResult, error)

Stats reads the local LLM/embedding stats sink, applies the query's range and field filters, and aggregates the result. Pure read — the only side effect is the underlying file read in the reader. SinkEmpty reflects whether the sink held any records at all, before filtering.

func (*Finder) SyncStatus added in v0.3.0

func (f *Finder) SyncStatus(ctx context.Context, q query.SyncStatusQuery) (model.SyncStatus, error)

SyncStatus processes a SyncStatusQuery into a SyncStatus. Cooldown is read from the finder's Config; callers supply SDDDir for the last-fetch marker. The finder touches last-fetch on every successful attempt and on terminal failure states (no remote, no upstream, fetch failure) so transient environment issues don't re-poll on every command.

func (*Finder) UnknownConfigKeys added in v0.17.0

UnknownConfigKeys reports the keys sdd read past because it does not know them — the one computation behind every surface that says so, rather than each deciding for itself what counts as unknown.

func (*Finder) WIPList

func (f *Finder) WIPList(q query.WIPListQuery) (*query.WIPListResult, error)

WIPList loads every active WIP marker from the wip/ subdirectory of q.GraphDir. Returns an empty result (not an error) when no markers exist.

func (*Finder) WritingGuide added in v0.17.0

func (f *Finder) WritingGuide(ctx context.Context, graph *model.Graph, q query.WritingGuideQuery) (*query.WritingGuideResult, error)

WritingGuide runs the writing guide against the draft in the query. The guide judges the draft in isolation from the dialogue and the graph around it — that scope is the check's design (d-cpt-20r) — but it reads with the framework's own kind knowledge: the graph supplies the type-system overview and the drafted kind's authoring fact, rendered into the prompt so kind-fit judgments run on meaning rather than bare tokens (s-tac-fu8). Returns an error only for infrastructure failures; findings, including none, are the result.

type GitSyncer added in v0.3.0

type GitSyncer interface {
	// InRepo reports whether the process is inside a git working tree.
	InRepo(ctx context.Context) bool
	// HasRemote reports whether the repository has at least one remote configured.
	HasRemote(ctx context.Context) bool
	// UpstreamRef returns the upstream ref name for the current branch
	// (e.g. "origin/main"). Empty string + nil error means the branch has
	// no upstream configured.
	UpstreamRef(ctx context.Context) (string, error)
	// Fetch runs `git fetch` with no args. A non-nil error means the fetch
	// attempt failed (network, auth, etc.).
	Fetch(ctx context.Context) error
	// CountCommits counts commits in rangeSpec whose messages match the
	// given grep pattern (ERE). rangeSpec is a git log range such as
	// "HEAD..@{u}" or "@{u}..HEAD".
	CountCommits(ctx context.Context, rangeSpec, grepPattern string) (int, error)
	// MergeTreePredict simulates a three-way merge in memory and returns
	// the list of paths that would conflict. An empty slice means clean.
	// The merge base is computed by git internally from (ourRef, theirRef)
	// — this keeps the surface compatible with git 2.38 (the explicit
	// --merge-base flag was added in 2.40).
	MergeTreePredict(ctx context.Context, ourRef, theirRef string) ([]string, error)
}

GitSyncer is the git surface consumed by the sync finder. Production shells out via exec.Command; tests inject stubs. Methods return errors only for unexpected failures — missing-repo / missing-remote / missing-upstream are expressed through the dedicated boolean / string predicates (InRepo, HasRemote, UpstreamRef) so the finder can report distinct SyncStates rather than collapsing everything into errors.

type GraphDocuments added in v0.17.0

type GraphDocuments struct {
	Entries    []EntryDocument
	WIP        []WIPDocument
	Unreadable []DocumentIssue
}

GraphDocuments is the full storage-neutral document set a source hands the GraphFinder: entry documents, WIP marker documents, and issues for content the source could not decode into a document.

type GraphFinder added in v0.17.0

type GraphFinder struct {
	// contains filtered or unexported fields
}

GraphFinder is the single shared graph reader. It holds a built model.Graph (and any WIP markers) and serves every read against it; the query-struct methods carry pure intent while the finder supplies the graph. Load errors are data it serves through Health, never a reason it dies: a document that fails to parse becomes a model.LoadIssue on the held graph and the rest still loads. It composes an inner *Finder for read dependencies (config, pre-flight runner, connected-repos registry), so each read is a single implementation.

func NewGraphFinder added in v0.17.0

func NewGraphFinder(source DocumentSource, opts Options) (*GraphFinder, error)

NewGraphFinder builds a GraphFinder from a document source — the one unified construction path both graph worlds (filesystem CLI loads, structured snapshots) flow through. Per-document parse failures and source-reported unreadable documents become LoadIssues on the held graph, never an abort. Base-entry assembly stays a hard error (the embedded set is compile-time shaped, so a failure is a broken build) and a malformed WIP marker stays a hard error (explicitly parked policy — current strictness kept).

func (*GraphFinder) Graph added in v0.17.0

func (gf *GraphFinder) Graph() *model.Graph

Graph returns the held graph. The same-module read seams (the engine Graphs provider, presenters) consume a *model.Graph directly.

func (*GraphFinder) Health added in v0.17.0

func (gf *GraphFinder) Health() model.GraphHealth

Health flattens the held graph's load failures and per-entry warnings into a summary — load errors are data the finder serves, not a reason it dies.

func (*GraphFinder) Lint added in v0.17.0

func (gf *GraphFinder) Lint(_ query.LintQuery) (*query.LintResult, error)

Lint runs the categorized graph-side lint providers (d-cpt-xc3): graph integrity (load errors, entry warnings, missing summaries) as error findings, and the procedure runtime (spec load, then the serve-budget arithmetic) — a spec that fails to load is an error, an overshooting one an advisory, because it still runs (d-tac-rzi). Index findings are filled in separately by IndexLint, whose inputs the shell resolves.

func (*GraphFinder) Preflight added in v0.17.0

Preflight runs the pre-flight validator against the given query. Runs Go-side mechanical checks first (see mechanicalPreflight), then delegates to the llm package for rubric-based checks, merging the findings so callers see a unified view. Returns an error only for infrastructure failures.

Mechanical checks cover participant coverage (AC 6), actor canonical write-once (AC 5), and role canonical-match + refs-head (AC 7) per plan d-cpt-d34. The former LLM-judged participant-drift check is retired in favor of the mechanical canonical check.

The language-drift check receives the configured graph language from config (see Finder.language). Empty means no language check (English default); a locale code activates the check against description prose.

func (*GraphFinder) ReadAttachment added in v0.17.0

ReadAttachment reads one page of an entry's attachment through the shared accessor: the entry's frontmatter-listed attachments are the only readable set, so callers never touch the on-disk layout (d-tac-d21). Pure read.

func (*GraphFinder) Show added in v0.17.0

func (gf *GraphFinder) Show(q query.ShowQuery) (*query.ShowResult, error)

Show resolves the entries named in q and returns groups with upstream and downstream chains. The heavy lifting (tree traversal, dedup, depth limiting) is delegated to model.Graph.BuildShowTree.

func (*GraphFinder) View added in v0.17.0

func (gf *GraphFinder) View(q query.ViewQuery) (*query.ViewResult, error)

View executes the layout pipeline in q.Layout against q.Graph and returns one SectionResult per section in source order. Per the design, every section must terminate in a render function (e.g. as-list); render is always the pipeline's terminus.

Sourcing dispatches on the first `source(<name>)` call in the section (default `graph` when absent): graph sections walk q.Graph through the filter chain; wip sections use q.WIPMarkers, falling back to f.LoadWIPMarkers(q.GraphDir), as a disjoint data set rendered by as-wip-list. The two paths share the section-walking shell but apply distinct primitive vocabularies; cross- path mixing (e.g. kind() over wip markers) errors with a source-aware message.

WIP markers are resolved once when any section in the layout uses source(wip), then the same slice is handed to every wip section. A nil marker slice and empty GraphDir errors at section evaluation; non-wip layouts ignore both inputs.

Unknown function names return an error listing the valid set so users (and future-slice tests) get a clear signal.

func (*GraphFinder) WIPMarkers added in v0.17.0

func (gf *GraphFinder) WIPMarkers() ([]*model.WIPMarker, error)

WIPMarkers returns the finder's WIP markers, resolving them lazily from the held graph's directory when none were attached (the CLI path). Callers that attached markers via WithWIP (the snapshot path) get those back directly.

func (*GraphFinder) WithWIP added in v0.17.0

func (gf *GraphFinder) WithWIP(wip []*model.WIPMarker) *GraphFinder

WithWIP attaches WIP markers the caller already holds (the snapshot path, whose graph carries no on-disk directory to lazy-load from). Returns the receiver for chaining.

type GraphSource added in v0.14.1

type GraphSource struct {
	// contains filtered or unexported fields
}

GraphSource is the read side's owner of "what the current graph is": a memoizing read-through over the single IO loader (LoadGraph), with an Invalidate hook that drops the memo after a write. It is the one seam every graph read flows through, so cross-repo assembly (model.MultiGraph) has exactly one insertion point — behind Current — with the side-effectful cache clone/pull staying handler-side, invalidating the source on completion.

One source per read scope. A long-lived reader that reads many times within one scope and mutates the graph mid-scope (the engine session across an advance) holds a source and invalidates it post-write. Short one-shot readers (a CLI command, an MCP free read, a handler validating before a write) call Finder.CurrentGraph, which sources once — no retained cache, matching the prior per-command / per-call lifetime.

func (*GraphSource) Current added in v0.14.1

func (gs *GraphSource) Current() (*model.Graph, error)

Current returns the current graph, loading it on first call and reusing the memoized value until Invalidate drops it. Concurrency-safe: the read side now owns this state, so a source shared across an engine advance is guarded.

func (*GraphSource) Invalidate added in v0.14.1

func (gs *GraphSource) Invalidate()

Invalidate drops the memo so the next Current reloads. Called after a write mutates the graph, so post-write reads in the same scope see the change.

type Options added in v0.3.0

type Options struct {
	PreflightRunner    llm.Runner
	WritingGuideRunner llm.Runner
	Config             *model.PerRepoConfig
	GitSyncer          GitSyncer
	// Repos is the pure read surface over the connected repos — the only
	// cross-repo capability a finder holds (no clone, no pull). Nil means no
	// connected-repos support: cross-repo refs stay unresolved.
	Repos *repos.Registry
	// ProcedureRegistry is the engine registration procedure specs load and
	// size against (d-tac-rzi): pre-flight's serve-budget arithmetic and
	// lint's procedure-runtime provider. Nil skips both checks.
	ProcedureRegistry *engine.Registry
}

Options configures a new Finder. Zero-valued fields mean "not available" — methods that need a dependency fall back to empty/nil behaviour rather than failing.

type SearchFinder added in v0.4.0

type SearchFinder struct {
	// contains filtered or unexported fields
}

SearchFinder is the read-side handler for sdd search. It composes three retrieval modes — text (live grep over .sdd/graph/), vector (chromem-go cosine search over chunks), and hybrid (RRF fusion when both are present) — and returns ranked entries with citation chunks.

Pure read: no side effects. The lazy-fill that precedes a query is the IndexHandler's job; this finder consumes the index as-is.

func NewSearchFinder added in v0.4.0

func NewSearchFinder(opts SearchFinderOptions) *SearchFinder

NewSearchFinder constructs a SearchFinder.

func (*SearchFinder) Search added in v0.4.0

Search dispatches the query to the appropriate mode.

func (*SearchFinder) VectorAvailable added in v0.4.0

func (f *SearchFinder) VectorAvailable() bool

VectorAvailable reports whether the configured dependencies allow vector or hybrid mode. Used by the CLI to render the `Search: text` vs `Search: vector,text` capability line in `sdd info`'s header.

type SearchFinderOptions added in v0.4.0

type SearchFinderOptions struct {
	Graph      *model.Graph
	GraphDir   string
	Embedder   llm.Embedder
	IndexStore *index.Index
	Repos      *repos.Registry
}

SearchFinderOptions configures NewSearchFinder. Graph is the graph the query filters and resolves against — the SearchFinder holds it so the SearchQuery stays pure intent. GraphDir is required (used by text mode to resolve attachment paths). Embedder + IndexStore are required for vector and hybrid modes; their absence makes those modes return an error. Repos is required for cross-repo search (MultiSearch).

type TopicFilter added in v0.5.0

type TopicFilter struct {
	Prefix model.TopicPath
}

TopicFilter selects entries whose effective topic set contains a label matching the given prefix path component-wise (case-insensitive).

"Effective topics" of an entry combine inline `topics:` declared on the entry's own frontmatter with topics declared by `kind: annotation` entries whose refs (or per-topic members sub-selection) include the entry. This merge is what makes annotations a first-class clustering mechanism — an entry is "in topic X" whether it tagged itself or some annotation tagged it.

Owned by Plan 1 (d-tac-gvn) per the augmenting directive d-tac-9q1; Plan 2 (d-tac-uww, sdd view) consumes this primitive for its `topic(L)` filter vocabulary in the pipeline grammar.

func (TopicFilter) ExcludeEntries added in v0.5.1

func (f TopicFilter) ExcludeEntries(g *model.Graph, entries []*model.Entry) []*model.Entry

ExcludeEntries returns the subset of entries whose effective topic set does NOT match the prefix. Mirror of FilterEntries for the not(topic(...)) negation primitive (d-tac-e1s). A zero prefix is treated as "no exclusion" — every entry passes through unchanged.

func (TopicFilter) FilterEntries added in v0.5.0

func (f TopicFilter) FilterEntries(g *model.Graph, entries []*model.Entry) []*model.Entry

FilterEntries returns the subset of entries whose effective topic set matches the prefix. Order is preserved; nil input returns nil.

func (TopicFilter) MatchEntry added in v0.5.0

func (f TopicFilter) MatchEntry(g *model.Graph, e *model.Entry) bool

MatchEntry reports whether the entry has any effective topic with prefix. Pure — uses the graph's reverse index for annotation lookups, no I/O.

type WIPDocument added in v0.17.0

type WIPDocument struct {
	Name    string
	Content string
}

WIPDocument is the storage-neutral form of one WIP marker: its file name (e.g. "handle.md") and raw content.

Jump to

Keyboard shortcuts

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