Documentation
¶
Overview ¶
Package issue — lifecycle transition orchestration for the Issue kind.
This file hosts ChangeStatus, the kind-specific orchestrator invoked by `specscore issue change-status`. It performs line-based YAML frontmatter rewriting for the `status:` field (and optionally `severity:`, `rejection_reason:`, `rejection_notes:` fields).
LINT INVOCATION lives in the cobra adapter (internal/cli/issue.go), NOT here, to avoid an import cycle: pkg/lint already imports pkg/issue for the issue-* lint rules, so pkg/issue cannot depend back on pkg/lint. The adapter passes a PostMutationHook into ChangeStatus; this package only knows "run the post-mutation hook, and roll back if it fails".
Package issue provides parsing and discovery for SpecScore `issue` artifacts. Issues are markdown files declaring `type: issue` in their YAML frontmatter and living at one of two canonical locations:
- spec/issues/<slug>.md (root-level)
- spec/features/<feature-slug>/issues/<slug>.md (Feature-scoped)
See spec/features/cli/spec/lint/issue-rules/README.md for the full contract.
Index ¶
- Constants
- Variables
- func IsLegalTransition(from, to string) bool
- func LegalTransitions() map[string][]string
- func Scaffold(opts ScaffoldOptions) ([]byte, error)
- func ValidateSlug(slug string) error
- type ChangeStatusOptions
- type ChangeStatusResult
- type Discovered
- type Issue
- type PostMutationHook
- type ScaffoldOptions
Constants ¶
const RuleFamilyPrefix = "I-"
RuleFamilyPrefix is the canonical ID prefix for every lint rule that validates `issue` artifacts (I-001 .. I-015).
const TypeValue = "issue"
TypeValue is the literal frontmatter value identifying an issue artifact.
Variables ¶
var PathPatterns = []string{
"issues/*.md",
"features/*/issues/*.md",
}
PathPatterns names the two glob patterns (relative to the spec root) where `issue` artifacts may live. Files outside these patterns that declare `type: issue` violate rule I-009 (dual-location).
The patterns are written without the leading `spec/` prefix because the lint engine always operates relative to the discovered spec root.
var ValidReasonValues = []string{"not-a-defect", "wont-fix", "duplicate", "not-reproducible", "by-design", "deferred"}
ValidReasonValues returns valid --reason values for rejection.
var ValidSeverityValues = map[string]bool{ "low": true, "medium": true, "high": true, "critical": true, }
ValidSeverityValues is the set of valid --severity values for issue new.
var ValidTargetStatuses = []string{"investigating", "resolved", "rejected"}
ValidTargetStatuses returns valid --to values for issue change-status.
Functions ¶
func IsLegalTransition ¶
IsLegalTransition checks if from→to is a valid issue transition.
func LegalTransitions ¶
LegalTransitions returns the issue transition matrix.
func Scaffold ¶
func Scaffold(opts ScaffoldOptions) ([]byte, error)
Scaffold returns a lint-clean Issue file body for the given options.
func ValidateSlug ¶
ValidateSlug checks that the slug conforms to issue slug rules: lowercase, [a-z0-9-] only, no leading/trailing hyphens, max 60 chars.
Types ¶
type ChangeStatusOptions ¶
type ChangeStatusOptions struct {
// SpecRoot is the project root that contains the `spec/` subtree
// (NOT the `spec/` directory itself).
SpecRoot string
// Slug is the Issue slug.
Slug string
// To is the target status (lowercase): investigating, resolved, rejected.
To string
// Severity is an optional severity override. Required when transitioning
// and current severity is absent.
Severity string
// Reason is required when To=rejected.
Reason string
// Notes is optional when To=rejected.
Notes string
// PostMutation is the post-rewrite hook (typically a spec-lint pass).
PostMutation PostMutationHook
}
ChangeStatusOptions packages the inputs to ChangeStatus.
type ChangeStatusResult ¶
ChangeStatusResult is the success payload returned by ChangeStatus.
func ChangeStatus ¶
func ChangeStatus(opts ChangeStatusOptions) (ChangeStatusResult, error)
ChangeStatus performs an Issue-kind lifecycle transition end-to-end.
Flow:
- Find the issue file by slug using DiscoverAll.
- Parse current status from frontmatter.
- Validate the transition is legal.
- Check severity gating.
- Check rejection gating (require Reason when To=rejected).
- Rewrite frontmatter fields.
- Invoke PostMutation hook; rollback on failure.
type Discovered ¶
type Discovered struct {
// Path is the absolute path to the file.
Path string
// RelPath is the path relative to the spec root, with forward slashes.
RelPath string
// Slug is the basename minus `.md`.
Slug string
// MatchesPattern is true when RelPath matches one of the two
// canonical PathPatterns. Files declaring `type: issue` outside
// the patterns have MatchesPattern == false (I-009 candidates).
MatchesPattern bool
// FeatureSlug, when non-empty, is the parent Feature slug for a
// Feature-scoped issue (RelPath of the form
// `features/<feature-slug>/issues/<slug>.md`). Empty for root-level
// issues or off-pattern issues.
FeatureSlug string
}
Discovered summarizes one file that declares `type: issue` in its YAML frontmatter, regardless of whether its path matches PathPatterns.
func DiscoverAll ¶
func DiscoverAll(specRoot string) ([]Discovered, error)
DiscoverAll walks the entire spec tree (skipping hidden dirs) and returns every markdown file whose YAML frontmatter declares `type: issue`. The walk is deliberately broad — restricting to the canonical PathPatterns would hide exactly the files I-009 needs to flag.
Returns ([], nil) when specRoot does not exist or is not a directory.
type Issue ¶
type Issue struct {
// Path is the absolute (or otherwise caller-supplied) path to the file.
Path string
// Slug is the basename minus `.md`.
Slug string
// Type is the frontmatter `type` value (empty when missing or
// unparseable). Only files where Type == TypeValue are considered
// issue artifacts.
Type string
// Frontmatter holds the top-level YAML mapping keys (values
// stringified). nil when no frontmatter could be parsed.
Frontmatter map[string]string
// FrontmatterKeyOrder preserves the order keys appeared in the
// source YAML.
FrontmatterKeyOrder []string
// Body is the markdown body after the closing `---`.
Body string
// HasFrontmatter is true when the file begins with `---` and a
// closing `---` line is found.
HasFrontmatter bool
// BugsRaw is the raw YAML node for the `bugs` frontmatter field, or
// nil when the field is absent. Exposed so list-aware rules (I-004)
// can inspect the node's Kind and elements without re-parsing.
BugsRaw *yaml.Node
}
Issue is the parsed representation of an issue artifact. It is deliberately minimal at this Task: only the fields the I-009 rule (and downstream rules in later tasks) need.
type PostMutationHook ¶
type PostMutationHook func() error
PostMutationHook is called after a successful status rewrite.
type ScaffoldOptions ¶
type ScaffoldOptions struct {
Slug string
Title string // defaults to title-cased slug
CapturedBy string // defaults to $USER
CapturedAt string // RFC 3339 timestamp, defaults to now UTC
Severity string // optional: low|medium|high|critical
AffectedComponent string // optional: Feature slug
FirstSeen string // optional
GithubIssue string // optional
}
ScaffoldOptions controls the content emitted by Scaffold.