Add cmd/docs, the first corestack component: `docs new adr|plan "Title"` writes records from embedded templates, and `docs check` validates them against the public format spec in docs/docs-format.md (timestamp IDs, frontmatter keys and enums, separate decision and implementation status, supersession symmetry, MADR 4 layout with required Consequences, plan sections, and repo-root/durable references), reporting path:line errors. Also adds the module skeleton (go.auvem.com/corestack, BSD-3-Clause, README, AGENTS.md, CLAUDE.md), Gitea CI running tests and docs check, a tag-triggered goreleaser release for darwin/linux binaries, and the accepted ADR adopting the format. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
46 lines
1.8 KiB
Go
46 lines
1.8 KiB
Go
package docs
|
|
|
|
import "time"
|
|
|
|
var (
|
|
adrStatuses = []string{"proposed", "accepted", "rejected", "deprecated", "superseded"}
|
|
adrImpls = []string{"not-started", "in-progress", "implemented", "deferred", "not-applicable"}
|
|
adrRefs = []refSpec{
|
|
{"supersedes", kAdr}, {"superseded_by", kAdr}, {"related", kAdr | kPlan},
|
|
{"tracking", kPlan | kURL}, {"implemented_by", kURL | kFile},
|
|
}
|
|
)
|
|
|
|
// adr checks the ADR frontmatter and returns its title for the body check.
|
|
func (c *checker) adr(r *record, m *fm) (string, bool) {
|
|
m.keys("title", "date", "status", "implementation", "summary", "tags", "supersedes", "superseded_by", "related", "tracking", "implemented_by")
|
|
title, titleOK := m.text("title")
|
|
if d, line, ok := m.scalar("date"); ok {
|
|
if _, err := time.Parse(time.DateOnly, d); err != nil || len(d) != len(time.DateOnly) {
|
|
c.add(r.path, line, "date must be a valid YYYY-MM-DD date")
|
|
} else if d != r.date {
|
|
c.add(r.path, line, "date %s does not match the filename date %s", d, r.date)
|
|
}
|
|
}
|
|
status, statusOK := m.enum("status", adrStatuses...)
|
|
impl, implOK := m.enum("implementation", adrImpls...)
|
|
m.text("summary")
|
|
m.tags()
|
|
m.refs(adrRefs...)
|
|
r.status = status
|
|
if statusOK {
|
|
if n := len(r.lists["superseded_by"]); status == "superseded" && n == 0 {
|
|
m.problemf("status", "status superseded requires superseded_by")
|
|
} else if status != "superseded" && n > 0 {
|
|
m.problemf("superseded_by", "superseded_by requires status superseded")
|
|
}
|
|
if status == "rejected" && implOK && impl != "not-applicable" {
|
|
m.problemf("implementation", "status rejected requires implementation not-applicable")
|
|
}
|
|
}
|
|
if implOK && impl == "implemented" && len(r.lists["implemented_by"]) == 0 {
|
|
m.problemf("implementation", "implementation implemented requires implemented_by")
|
|
}
|
|
return title, titleOK
|
|
}
|