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.