Documentation
¶
Overview ¶
Package markdown provides parsing and mutation for SPEC.md files. It operates on line-level patterns, not a full AST — sufficient for the structured SPEC.md format.
Index ¶
- Constants
- func AppendDecision(path, question, user string) (int, error)
- func AppendTriageComment(path, actor, message string) error
- func ArchiveTriageItem(path, reason, note, actor string) error
- func Body(content string) string
- func DefaultSpecTemplate() string
- func DefaultTriageTemplate() string
- func EscalateTriageItem(path string, currentlyUrgent bool, actor string) error
- func FormatDecisionTable(entries []DecisionEntry) string
- func IsSectionNonEmpty(sections []Section, slug string) bool
- func IsValidSectionSlug(slug string) bool
- func MaxSpecNum(existingFiles []string) int
- func MaxTriageNum(existingFiles []string) int
- func ReadTeamTemplate(kind TemplateKind, repoDir, configuredPath string) (content, path string, ok bool)
- func RenderSpec(tpl string, fields SpecFields, defaults []KV) (string, error)
- func RenderTriage(tpl string, fields TriageFields) (string, error)
- func ReplaceSection(path, slug, newContent string) error
- func ReplaceSectionContent(content, slug, newContent string) (string, error)
- func ResolveAnchorTokens(body []AnchorToken, quote, prefix string) (idx int, ok, ambiguous bool)
- func ResolveAnchorTokensLoose(body []AnchorToken, quote, prefix string) (idx int, ok, ambiguous bool)
- func ResolveDecision(path string, number int, decision, rationale, user string) error
- func ResolveTemplate(kind TemplateKind, repoDir, configuredPath string) (content, source string)
- func ScaffoldSpec(id, title, author, cycle, source string) string
- func ScaffoldSpecFromConfig(repoDir string, tc TemplateConfig, fields SpecFields) string
- func ScaffoldTriage(id, title, priority, source, sourceRef, reportedBy string) string
- func ScaffoldTriageFromConfig(repoDir string, tc TemplateConfig, fields TriageFields) string
- func UpdateFrontmatter(content string, meta *SpecMeta) (string, error)
- func UpdateTriageFields(path, title, priority, source, body string) error
- func ValidSectionSlugs() []string
- func WriteMeta(path string, meta *SpecMeta) error
- func WriteTriageMeta(path string, meta *TriageMeta) error
- type AnchorMatch
- type AnchorToken
- type BlockRange
- type BuildStep
- type DecisionEntry
- type KV
- type ReviewApproval
- type ReviewState
- type Section
- type SpecFields
- type SpecMeta
- func (m *SpecMeta) AllStepsComplete() bool
- func (m *SpecMeta) CurrentStep() int
- func (m *SpecMeta) HasAssignee(identity string) bool
- func (m *SpecMeta) IsReviewApproved() bool
- func (m *SpecMeta) IsReviewChangesRequested() bool
- func (m *SpecMeta) IsReviewPending() bool
- func (m *SpecMeta) StepsExist() bool
- type TemplateConfig
- type TemplateIssue
- type TemplateKind
- type TriageComment
- type TriageFields
- type TriageMeta
Constants ¶
const ( StepStatusPending = "pending" StepStatusInProgress = "in-progress" StepStatusComplete = "complete" StepStatusBlocked = "blocked" )
Step status constants.
const ( ReviewStatusPending = "pending" ReviewStatusApproved = "approved" ReviewStatusChangesRequested = "changes_requested" )
Review status constants.
const ( DefaultSpecPath = "templates/spec.md" DefaultTriagePath = "templates/triage.md" )
Default spec/triage template paths inside the specs repo.
Variables ¶
This section is empty.
Functions ¶
func AppendDecision ¶
AppendDecision adds a new question to the decision log in a file. Returns the assigned decision number.
func AppendTriageComment ¶ added in v0.18.0
AppendTriageComment appends a new comment to a triage file's history log and persists it in place. It is additive-only; existing entries are never touched.
func ArchiveTriageItem ¶ added in v0.18.0
ArchiveTriageItem sets a triage item's status to "archived", records the resolution timestamp, and appends a closing history entry.
func DefaultSpecTemplate ¶ added in v0.37.0
func DefaultSpecTemplate() string
DefaultSpecTemplate returns the embedded default spec template.
func DefaultTriageTemplate ¶ added in v0.37.0
func DefaultTriageTemplate() string
DefaultTriageTemplate returns the embedded default triage template.
func EscalateTriageItem ¶ added in v0.18.0
EscalateTriageItem toggles severity between "urgent" and "" (normal) and appends a history entry recording the escalation event.
func FormatDecisionTable ¶
func FormatDecisionTable(entries []DecisionEntry) string
FormatDecisionTable renders decision entries as a markdown table.
func IsSectionNonEmpty ¶
IsSectionNonEmpty checks if a section has meaningful content (not just whitespace).
func IsValidSectionSlug ¶
IsValidSectionSlug checks if the slug is a known section.
func MaxSpecNum ¶ added in v0.12.1
MaxSpecNum returns the highest SPEC-NNN number among the given filenames, or 0 if none. It is the bootstrap seed for the counter ref (SPEC-018 §7.1).
func MaxTriageNum ¶ added in v0.12.1
MaxTriageNum returns the highest TRIAGE-NNN number among the given filenames, or 0 if none.
func ReadTeamTemplate ¶ added in v0.37.0
func ReadTeamTemplate(kind TemplateKind, repoDir, configuredPath string) (content, path string, ok bool)
ReadTeamTemplate reads the raw team template file for a kind from repoDir/configuredPath (defaulting to the conventional path). It returns the file content, the resolved path, and whether the file exists. No validation is performed — callers that need the effective (safe) template should use ResolveTemplate instead.
func RenderSpec ¶ added in v0.37.0
func RenderSpec(tpl string, fields SpecFields, defaults []KV) (string, error)
RenderSpec renders a spec template with the given fields and optional frontmatter defaults. Defaults whose key already appears in the rendered frontmatter are skipped, so computed fields always win.
func RenderTriage ¶ added in v0.37.0
func RenderTriage(tpl string, fields TriageFields) (string, error)
RenderTriage renders a triage template with the given fields.
func ReplaceSection ¶
ReplaceSection replaces the content of a section in a file.
func ReplaceSectionContent ¶
ReplaceSectionContent replaces a section's content in the markdown string.
func ResolveAnchorTokens ¶ added in v0.35.0
func ResolveAnchorTokens(body []AnchorToken, quote, prefix string) (idx int, ok, ambiguous bool)
ResolveAnchorTokens finds one uniquely identified quote occurrence.
Matching runs over a concatenated character stream of the normalised tokens rather than token-by-token: renderers move word boundaries (inline code gains padding spaces, long words reflow), so `n`/`p` may tokenise as one word in markdown source and two in rendered output. Character-stream matching is immune to boundary drift while staying whitespace-, markup-, and case-insensitive.
func ResolveAnchorTokensLoose ¶ added in v0.35.0
func ResolveAnchorTokensLoose(body []AnchorToken, quote, prefix string) (idx int, ok, ambiguous bool)
ResolveAnchorTokensLoose matches like ResolveAnchorTokens but tolerates renderer-side truncation: Glamour clips long code lines and ellipsises wide table cells, so a block's full text may be absent from rendered output even though its head is on screen. It binary-searches the longest head of the quote that still occurs (occurrence is monotone in head length) and accepts it when the head is either the whole quote or long enough to be distinctive. Ambiguity still degrades — the head must identify exactly one position.
func ResolveDecision ¶
ResolveDecision updates an existing decision log entry.
func ResolveTemplate ¶ added in v0.37.0
func ResolveTemplate(kind TemplateKind, repoDir, configuredPath string) (content, source string)
ResolveTemplate returns the effective template content for a kind: the team file at repoDir/configuredPath if present and free of fatal validation issues (parse/render errors, unresolved placeholders, missing gate-critical sections), else the embedded default. The returned source is "team" or "default".
Falling back — rather than erroring — keeps scaffolding forgiving of a fluid team template: a template broken mid-edit degrades to the built-in default with a warning instead of blocking spec creation.
func ScaffoldSpec ¶
ScaffoldSpec generates a new SPEC.md from the embedded default template. It is the zero-config entry point; use ScaffoldSpecFromConfig to honour a committed team template and frontmatter defaults.
func ScaffoldSpecFromConfig ¶ added in v0.37.0
func ScaffoldSpecFromConfig(repoDir string, tc TemplateConfig, fields SpecFields) string
ScaffoldSpecFromConfig resolves the effective spec template (team file first, embedded default fallback) and renders it with the given fields and frontmatter defaults. A render error in a team template falls back to the embedded default so `spec new` never blocks on a broken template.
func ScaffoldTriage ¶
ScaffoldTriage generates a new TRIAGE.md from the embedded default template.
func ScaffoldTriageFromConfig ¶ added in v0.37.0
func ScaffoldTriageFromConfig(repoDir string, tc TemplateConfig, fields TriageFields) string
ScaffoldTriageFromConfig resolves the effective triage template and renders it with the given fields.
func UpdateFrontmatter ¶
UpdateFrontmatter returns content with updated frontmatter, preserving the body. It also updates the 'updated' field to today's date.
func UpdateTriageFields ¶ added in v0.18.0
UpdateTriageFields updates the mutable fields of a triage file and replaces the body, while preserving existing comments and immutable metadata.
func ValidSectionSlugs ¶
func ValidSectionSlugs() []string
ValidSectionSlugs returns all valid section slugs from the spec template.
func WriteTriageMeta ¶ added in v0.18.0
func WriteTriageMeta(path string, meta *TriageMeta) error
WriteTriageMeta updates triage frontmatter in a file, preserving the body.
Types ¶
type AnchorMatch ¶ added in v0.35.0
AnchorMatch locates a quote within section content. Ambiguous means the quote exists more than once and its prefix did not identify one occurrence.
func ResolveAnchor ¶ added in v0.35.0
func ResolveAnchor(sectionBody, quote, prefix string) AnchorMatch
ResolveAnchor finds quote within sectionBody. An ambiguous match is a graceful miss: callers degrade to the section rather than silently choosing the first occurrence.
type AnchorToken ¶ added in v0.35.0
AnchorToken is one normalised word tagged with its original position.
func TokenizeAnchor ¶ added in v0.35.0
func TokenizeAnchor(text string) []AnchorToken
TokenizeAnchor tokenises text into normalised words with source positions. Normalisation folds case and drops every non-alphanumeric rune — interior ones included — so markdown inline markup (`n`/`p`, **bold**, smart quotes) tokenises identically on the source and rendered sides of a Glamour render.
type BlockRange ¶ added in v0.35.0
BlockRange is one source block, expressed as [StartLine, EndLine). Blocks are the units the reader can quote: paragraphs, individual list items, individual table rows, fenced blocks, and blockquote paragraphs.
func BlockRanges ¶ added in v0.35.0
func BlockRanges(source string) []BlockRange
BlockRanges splits markdown into stable reader anchor units. This is a source-oriented scanner rather than a renderer concern; it deliberately keeps list items and table rows separate even without blank lines.
type BuildStep ¶
type BuildStep struct {
// Repo is the repository this step targets.
Repo string `yaml:"repo"`
// Description is a brief summary of what this step accomplishes.
Description string `yaml:"description"`
// Branch is the git branch name for this step (auto-generated if empty).
Branch string `yaml:"branch,omitempty"`
// PR is the pull request number once created.
PR int `yaml:"pr,omitempty"`
// StoryKey is the linked PM story key for this step (e.g. "PLAT-201"),
// set when story sync is enabled so board analytics track build progress.
StoryKey string `yaml:"story_key,omitempty"`
// Status tracks the step's progress: pending, in-progress, complete, blocked.
Status string `yaml:"status"`
// BlockedReason explains why this step is blocked (when Status == "blocked").
BlockedReason string `yaml:"blocked_reason,omitempty"`
}
BuildStep represents a single step in the build plan. Steps are a checklist of work items, not git-level PR stacking.
type DecisionEntry ¶
type DecisionEntry struct {
Number int
Question string
Options string
Decision string
Rationale string
DecidedBy string
Date string
}
DecisionEntry represents a row in the decision log table.
func ParseDecisionLog ¶
func ParseDecisionLog(content string) ([]DecisionEntry, error)
ParseDecisionLog extracts decision entries from markdown content.
func ParseDecisionLogFromFile ¶
func ParseDecisionLogFromFile(path string) ([]DecisionEntry, error)
ParseDecisionLogFromFile reads a spec file and extracts the decision log.
type ReviewApproval ¶
type ReviewApproval struct {
Reviewer string `yaml:"reviewer"`
ApprovedAt string `yaml:"approved_at"`
}
ReviewApproval records a single reviewer's approval.
type ReviewState ¶
type ReviewState struct {
// RequestedAt is when the review was requested.
RequestedAt string `yaml:"requested_at,omitempty"`
// Reviewers is the list of users/roles who should review.
Reviewers []string `yaml:"reviewers,omitempty"`
// Approvals records who has approved and when.
Approvals []ReviewApproval `yaml:"approvals,omitempty"`
// Status is the overall review state: pending, approved, changes_requested.
Status string `yaml:"status"`
// Feedback contains reviewer feedback when changes are requested.
Feedback string `yaml:"feedback,omitempty"`
}
ReviewState tracks plan review status for async approval workflow.
type Section ¶
type Section struct {
Slug string // e.g., "problem_statement"
Heading string // e.g., "## 1. Problem Statement"
Level int // heading level (2 = ##, 3 = ###)
Owner string // from <!-- owner: role --> marker, or "auto"
Content string // raw markdown content (excluding heading line)
StartLine int // line number in the source file (1-indexed)
EndLine int // line number (exclusive)
}
Section represents a parsed markdown section with ownership.
func ExtractSections ¶
ExtractSections parses markdown content into sections.
func ExtractSectionsFromFile ¶
ExtractSectionsFromFile reads a file and extracts sections.
func FindSection ¶
FindSection returns the section with the given slug, or nil.
type SpecFields ¶ added in v0.37.0
type SpecFields struct {
ID, Title, Author, Cycle, Source, Date string
}
SpecFields are the scaffold fields available to a spec template.
type SpecMeta ¶
type SpecMeta struct {
ID string `yaml:"id"`
Title string `yaml:"title"`
Status string `yaml:"status"`
Version string `yaml:"version"`
Author string `yaml:"author"`
Cycle string `yaml:"cycle"`
EpicKey string `yaml:"epic_key,omitempty"`
Repos []string `yaml:"repos,omitempty"`
RevertCount int `yaml:"revert_count"`
Source string `yaml:"source,omitempty"`
Created string `yaml:"created"`
Updated string `yaml:"updated"`
// StageEnteredAt records when the spec entered its current stage (RFC3339).
// It drives the time-urgency gradient's dwell calculation and is stamped on
// every stage transition (advance/revert/eject/resume). Unlike Updated it is
// NOT reset by ordinary edits, so editing a spec does not reset its
// staleness. Legacy specs without it fall back to Updated.
StageEnteredAt string `yaml:"stage_entered_at,omitempty"`
// Assignees are the people responsible for moving the spec at its current
// stage. Drives personal dashboard scope (assignee-scoped stages). Empty
// means the spec is unclaimed and surfaces to the whole owning role.
Assignees []string `yaml:"assignees,omitempty"`
// BlockedFrom records the stage a spec was in when it was ejected to
// `blocked`, so the dashboard can role-scope the BLOCKED section and
// `spec resume` can restore the stage without parsing the escape-hatch log.
BlockedFrom string `yaml:"blocked_from,omitempty"`
// Steps is the structured build plan.
// Replaces unstructured §7.3 prose with authoritative step tracking.
Steps []BuildStep `yaml:"steps,omitempty"`
// Review tracks plan review state for the engineering stage.
Review *ReviewState `yaml:"review,omitempty"`
// FastTrack marks this as a fast-track bug fix (skips ceremony stages).
FastTrack bool `yaml:"fast_track,omitempty"`
}
SpecMeta represents the YAML frontmatter of a SPEC.md file.
func (*SpecMeta) AllStepsComplete ¶
AllStepsComplete returns true if all steps have status "complete".
func (*SpecMeta) CurrentStep ¶
CurrentStep returns the index (0-based) of the first non-complete step, or -1 if all steps are complete or there are no steps.
func (*SpecMeta) HasAssignee ¶ added in v0.22.1
HasAssignee reports whether identity (a user name or handle) is one of the spec's assignees. Matching is case-insensitive and tolerates a leading '@' on either side, mirroring how reviewers are matched elsewhere.
func (*SpecMeta) IsReviewApproved ¶
IsReviewApproved returns true if the review status is "approved".
func (*SpecMeta) IsReviewChangesRequested ¶
IsReviewChangesRequested returns true if reviewer requested changes.
func (*SpecMeta) IsReviewPending ¶
IsReviewPending returns true if review was requested but not yet completed.
func (*SpecMeta) StepsExist ¶
StepsExist returns true if at least one step is defined.
type TemplateConfig ¶ added in v0.37.0
TemplateConfig is the markdown-local view of a team's templates config. It is built from config.ResolvedConfig so this package stays decoupled from internal/config.
type TemplateIssue ¶ added in v0.37.0
TemplateIssue is a single validation finding. Fatal issues fail validation; non-fatal issues are reported as warnings.
func ValidateTemplate ¶ added in v0.37.0
func ValidateTemplate(kind TemplateKind, content string) []TemplateIssue
ValidateTemplate parses, renders, and structurally checks a template. It returns fatal issues (parse errors, unresolved placeholders, missing gate-critical sections) and non-fatal issues (level-2 sections lacking an owner/auto marker). The embedded default validates clean on fatal issues.
type TemplateKind ¶ added in v0.37.0
type TemplateKind int
TemplateKind selects which template a resolve/render/validate call targets.
const ( // SpecTemplate is the SPEC-NNN.md skeleton. SpecTemplate TemplateKind = iota // TriageTemplate is the TRIAGE-NNN.md skeleton. TriageTemplate )
type TriageComment ¶ added in v0.18.0
type TriageComment struct {
Actor string `yaml:"actor"`
Message string `yaml:"message"`
At string `yaml:"at"` // RFC3339 timestamp
}
TriageComment is a single immutable entry in a triage item's history log.
type TriageFields ¶ added in v0.37.0
type TriageFields struct {
ID, Title, Priority, Source, SourceRef, ReportedBy, Date string
}
TriageFields are the scaffold fields available to a triage template.
type TriageMeta ¶
type TriageMeta struct {
ID string `yaml:"id"`
Title string `yaml:"title"`
Status string `yaml:"status"`
Priority string `yaml:"priority"`
Severity string `yaml:"severity,omitempty"` // "urgent" or "normal" (default normal)
LinkedSpec string `yaml:"linked_spec,omitempty"` // SPEC-NNN if manually linked
Source string `yaml:"source,omitempty"`
SourceRef string `yaml:"source_ref,omitempty"`
ReportedBy string `yaml:"reported_by,omitempty"`
Created string `yaml:"created"`
ResolvedAt string `yaml:"resolved_at,omitempty"` // set when closed/archived
Comments []TriageComment `yaml:"comments,omitempty"` // append-only history log
}
TriageMeta represents the YAML frontmatter of a TRIAGE.md file.
func ParseTriageMeta ¶
func ParseTriageMeta(content string) (*TriageMeta, error)
ParseTriageMeta parses triage YAML frontmatter.
func ReadTriageMeta ¶
func ReadTriageMeta(path string) (*TriageMeta, error)
ReadTriageMeta reads and parses triage frontmatter.