feat: add docs checker for ADRs and implementation plans (#1)
release / release (push) Successful in 17s
ci / check (push) Successful in 18s

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>
This commit was merged in pull request #1.
This commit is contained in:
endandClaude Opus 5.5 committed 2026-10-23 15:56:03 -07:00
1 parent 37b0516fd2
commit cc482d9a9b
27 files changed
+2464

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
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
}