Skip to content

Package internal/specs

Import path: go.devnw.com/canary/internal/specs

package specs // import "go.devnw.com/canary/internal/specs"

API (raw go doc)


package specs // import "go.devnw.com/canary/internal/specs"

CANARY: REQ=CBIN-134; FEATURE="ExactIDLookup"; ASPECT=Engine; STATUS=IMPL;
UPDATED=2025-10-16

CANARY: REQ=CBIN-134; FEATURE="SectionLoader"; ASPECT=Engine; STATUS=IMPL;
UPDATED=2025-10-16

FUNCTIONS

func FindSpecByID(reqID string) (string, error)
    FindSpecByID locates spec.md file by exact requirement ID Uses glob pattern:
    .canary/specs/CBIN-XXX-*/spec.md

func FindSpecBySearch(query string, limit int) ([]matcher.Match, error)
    FindSpecBySearch performs fuzzy search across spec directories Reuses
    CBIN-133 fuzzy matcher for scoring and ranking

func FindSpecInDB(db *storage.DB, reqID string) (string, error)
    FindSpecInDB queries database for fast spec lookup (optional fallback) If
    database is unavailable, returns error and caller should use FindSpecByID

func ListSections(content string) ([]string, error)
    ListSections returns all section headers from markdown content Extracts all
    ## level headers

func ParseSections(content string, sections []string) (string, error)
    ParseSections extracts specific sections from markdown content If sections
    is empty, returns full content Preserves metadata at top (lines before first
    ##) Section names are case-insensitive


TYPES

type Dependency struct {
    // Source is the requirement ID that has the dependency (e.g., "CBIN-147")
    Source string

    // Target is the requirement ID being depended upon (e.g., "CBIN-146")
    Target string

    // Type indicates whether this is a full, partial feature, or aspect dependency
    Type DependencyType

    // RequiredFeatures lists specific features needed for PartialFeatures dependencies.
    // Only populated when Type is DependencyTypePartialFeatures.
    RequiredFeatures []string

    // RequiredAspect specifies the aspect needed for PartialAspect dependencies.
    // Only populated when Type is DependencyTypePartialAspect.
    RequiredAspect string

    // Description provides human-readable context about why this dependency exists.
    // This is optional and used for documentation purposes.
    Description string
}
    Dependency represents a dependency relationship between two requirements.
    It captures the source requirement, target requirement, type of dependency,
    and any specific features or aspects that must be satisfied.

func ParseDependencies(sourceReqID string, reader io.Reader) ([]Dependency, error)
    ParseDependencies parses dependency declarations from a spec.md file reader.
    It looks for the "## Dependencies" section and extracts all dependency
    declarations.

    Supported formats: - Full: "- CBIN-123 (Description)" - Partial Features:
    "- CBIN-123:Feature1,Feature2 (Description)" - Partial Aspect: "-
    CBIN-123:AspectName (Description)"

    Returns a slice of Dependency objects. Returns empty slice if no
    dependencies found.

func ParseDependenciesFromFile(sourceReqID, specPath string) ([]Dependency, error)
    ParseDependenciesFromFile reads a spec.md file and extracts all
    dependencies. Returns a slice of Dependency objects or an error if the file
    cannot be read.

type DependencyGraph struct {
    // Nodes maps requirement IDs to their list of outgoing dependencies.
    // Key: Source requirement ID (e.g., "CBIN-147")
    // Value: List of dependencies where this requirement is the source
    Nodes map[string][]Dependency
}
    DependencyGraph represents the complete dependency graph for all
    requirements. It provides methods for querying, traversal, and cycle
    detection.

func NewDependencyGraph() *DependencyGraph
    NewDependencyGraph creates a new empty DependencyGraph.

func (dg *DependencyGraph) AddDependency(dep Dependency)
    AddDependency adds a dependency to the graph. If the source node doesn't
    exist, it creates it.

func (dg *DependencyGraph) GetAllRequirements() []string
    GetAllRequirements returns all unique requirement IDs in the graph (both
    sources and targets).

func (dg *DependencyGraph) GetDependencies(reqID string) []Dependency
    GetDependencies returns all dependencies for a given requirement ID. Returns
    an empty slice if the requirement has no dependencies.

func (dg *DependencyGraph) GetReverseDependencies(reqID string) []Dependency
    GetReverseDependencies returns all requirements that depend on the given
    requirement ID. This answers the question: "What would be blocked if this
    requirement changes?"

type DependencyStatus struct {
    // Dependency is the dependency being evaluated
    Dependency Dependency

    // IsSatisfied indicates whether the dependency requirements are met.
    // For Full: All features of target are TESTED or BENCHED
    // For PartialFeatures: All RequiredFeatures are TESTED or BENCHED
    // For PartialAspect: All features of RequiredAspect are TESTED or BENCHED
    IsSatisfied bool

    // Blocking indicates whether this unsatisfied dependency blocks implementation.
    // Set to true when IsSatisfied is false.
    Blocking bool

    // Message provides human-readable explanation of the status.
    // Examples:
    // - "All required features are TESTED"
    // - "Waiting for CBIN-146:ProjectRegistry to reach TESTED status (currently IMPL)"
    // - "Target requirement CBIN-999 does not exist"
    Message string

    // MissingFeatures lists features that are not yet in TESTED/BENCHED status.
    // Only populated for PartialFeatures dependencies when IsSatisfied is false.
    MissingFeatures []string

    // CurrentStatus describes the current status of the target requirement or features.
    // Used for debugging and reporting.
    CurrentStatus string
}
    DependencyStatus represents the current satisfaction status of a dependency.
    It indicates whether the dependency is satisfied and provides contextual
    information.

type DependencyType int
    DependencyType represents the type of dependency relationship between
    requirements.

const (
    // DependencyTypeFull indicates the entire target requirement must be complete
    // (all features must be in TESTED or BENCHED status).
    DependencyTypeFull DependencyType = iota

    // DependencyTypePartialFeatures indicates only specific features of the target
    // requirement must be complete.
    DependencyTypePartialFeatures

    // DependencyTypePartialAspect indicates all features of a specific aspect
    // of the target requirement must be complete.
    DependencyTypePartialAspect
)
func (dt DependencyType) String() string
    String returns a human-readable string representation of the DependencyType.

type DependencyValidator struct {
    // Has unexported fields.
}
    DependencyValidator validates dependency graphs for cycles and missing
    requirements.

func NewDependencyValidator(graph *DependencyGraph) *DependencyValidator
    NewDependencyValidator creates a new dependency validator for the given
    graph.

func (dv *DependencyValidator) SetSpecFinder(finder SpecFinder)
    SetSpecFinder configures the validator to check for missing requirements.

func (dv *DependencyValidator) Validate() ValidationResult
    Validate performs comprehensive validation of the dependency graph.
    It checks for: 1. Circular dependencies (using DFS with recursion stack) 2.
    Missing requirements (if SpecFinder is configured)

type GraphGenerator struct {
    // Has unexported fields.
}
    GraphGenerator builds and visualizes dependency graphs.

func NewGraphGenerator(loader SpecLoader) *GraphGenerator
    NewGraphGenerator creates a new graph generator.

func (gg *GraphGenerator) BuildGraph(reqIDs []string) (*DependencyGraph, error)
    BuildGraph builds a complete dependency graph from a list of requirement
    IDs. It loads dependencies for each requirement and constructs the full
    graph.

func (gg *GraphGenerator) FormatASCIITree(graph *DependencyGraph, rootReqID string) string
    FormatASCIITree generates an ASCII tree visualization of the dependency
    graph. Shows the structure with Unicode box-drawing characters and optional
    status indicators.

    Example output: CBIN-147 (Specification Dependencies) ├── CBIN-146
    (Multi-Project Support) ✅ │ └── CBIN-129 (Migrations) ❌ └── CBIN-145 (Legacy
    Migration) ✅

func (gg *GraphGenerator) FormatCompactList(graph *DependencyGraph, reqID string) string
    FormatCompactList formats dependencies as a compact comma-separated list.
    Example: "CBIN-146, CBIN-145, CBIN-129"

func (gg *GraphGenerator) FormatDependencyChain(reqIDs []string) string
    FormatDependencyChain formats a list of requirement IDs as a chain. Example:
    "CBIN-147 → CBIN-146 → CBIN-129"

func (gg *GraphGenerator) FormatDependencySummary(graph *DependencyGraph, reqID string) string
    FormatDependencySummary generates a multi-line summary of dependencies.
    Includes direct dependencies, transitive count, and depth.

func (gg *GraphGenerator) GetDependencyDepth(graph *DependencyGraph, reqID string) int
    GetDependencyDepth returns the maximum depth of the dependency tree.
    Depth is the longest path from the root to any leaf node.

func (gg *GraphGenerator) GetTransitiveDependencies(graph *DependencyGraph, reqID string) []string
    GetTransitiveDependencies returns all transitive dependencies of a
    requirement. Uses BFS to traverse the dependency graph and collect all
    reachable requirements.

func (gg *GraphGenerator) SetStatusChecker(checker StatusCheckerInterface)
    SetStatusChecker configures the generator to show dependency status in
    visualizations.

type SpecFinder interface {
    // SpecExists checks if a specification exists for the given requirement ID
    SpecExists(reqID string) bool

    // FindSpecPath returns the path to the spec.md file for a requirement
    FindSpecPath(reqID string) (string, error)
}
    SpecFinder is an interface for finding and checking specification existence.
    This allows validation against the actual filesystem or a mock for testing.

type SpecLoader interface {
    // LoadDependencies loads all dependencies for a given requirement ID
    LoadDependencies(reqID string) ([]Dependency, error)
}
    SpecLoader is an interface for loading dependencies from spec files.

type StatusChecker struct {
    // Has unexported fields.
}
    StatusChecker checks whether dependencies are satisfied based on CANARY
    token status.

func NewStatusChecker(provider TokenProvider) *StatusChecker
    NewStatusChecker creates a new status checker with the given token provider.

func (sc *StatusChecker) CheckAllDependencies(deps []Dependency) []DependencyStatus
    CheckAllDependencies checks all dependencies and returns their statuses.

func (sc *StatusChecker) CheckDependency(dep Dependency) DependencyStatus
    CheckDependency checks whether a single dependency is satisfied.
    Satisfaction rules: - Full: All features of target requirement must be
    TESTED or BENCHED - PartialFeatures: All RequiredFeatures must be TESTED or
    BENCHED - PartialAspect: All features of RequiredAspect must be TESTED or
    BENCHED

    IMPL status is NOT sufficient - dependencies require tests.

func (sc *StatusChecker) FormatBlockingReport(deps []Dependency) string
    FormatBlockingReport generates a human-readable report of blocking
    dependencies.

func (sc *StatusChecker) GetBlockingDependencies(deps []Dependency) []DependencyStatus
    GetBlockingDependencies returns only the dependencies that are blocking (not
    satisfied).

type StatusCheckerInterface interface {
    // IsDependencySatisfied checks if a dependency is satisfied
    IsDependencySatisfied(dep Dependency) bool
}
    StatusChecker interface for checking dependency satisfaction

type TokenInfo struct {
    ReqID   string
    Feature string
    Aspect  string
    Status  string
}
    TokenInfo represents a CANARY token from storage. This is used by
    StatusChecker to query token status.

type TokenProvider interface {
    // GetTokensByReqID returns all CANARY tokens for a given requirement ID
    GetTokensByReqID(reqID string) []TokenInfo
}
    TokenProvider is an interface for retrieving CANARY tokens from storage.
    This allows the status checker to query token status without tight coupling
    to storage.

type ValidationResult struct {
    // IsValid is true if the dependency graph is valid (no cycles, all requirements exist)
    IsValid bool

    // Cycles contains all detected circular dependencies.
    // Each cycle is represented as a slice of requirement IDs forming the cycle.
    // Example: ["CBIN-100", "CBIN-101", "CBIN-102", "CBIN-100"]
    Cycles [][]string

    // MissingRequirements lists requirement IDs that are referenced but don't exist
    MissingRequirements []string

    // Errors contains human-readable error messages
    Errors []string
}
    ValidationResult contains the results of dependency validation.

func (vr *ValidationResult) FormatErrors() string
    FormatErrors returns a formatted string containing all validation errors.