Skip to content

feat(cli): saga lint — spec v1.0 conformance validator (#19)#73

Merged
mopanc merged 2 commits into
mainfrom
feat/saga-lint-spec-validator
May 11, 2026
Merged

feat(cli): saga lint — spec v1.0 conformance validator (#19)#73
mopanc merged 2 commits into
mainfrom
feat/saga-lint-spec-validator

Conversation

@mopanc
Copy link
Copy Markdown
Owner

@mopanc mopanc commented May 11, 2026

Summary

Bundle F of the v1.0.0-rc.1 plan — closes the last Sprint-0 issue and the Level-2 conformance gate.

saga lint walks every topic in active layers and validates against Saga Topic Spec v1.0. It's the missing piece for spec §10 Level 2 ("MUST implement lint covering: dangling relations, cycle detection, type validity, sensitivity defaults").

Rule coverage

Eleven diagnostic categories, with stable JSON category strings that tooling can filter on:

Category Severity Spec ref
parse-error error (exit 2) §2
missing-field error §2.1
invalid-type error §4
invalid-enum error §5 (all five traits + sensitivity)
scope-mismatch error §2.1
unknown-operator warn §6.2
dangling-relation error §6.1
cycle error §6.3 (supersedes + derived_from)
slug-mismatch warn §1.1
duplicate-id error §1.1
missing-recommended warn (fixable) §2.2 / §5.1 default

Flags

  • --scope <name> — restrict to a single layer scope
  • --fix — apply safe insertions only (currently confidence: tentative per spec §5.1 default). Never touches body or relations.
  • --format human|json — JSON for tooling, human for terminals

Exit codes: 0 clean / 1 findings / 2 parse errors.

Smoke run against the maintainer's real personal layer (48 notes)

  • 0 parse errors
  • 33 `slug-mismatch` warnings (titles legitimately evolved post-rename; the filename stays as the original slug — these are correctly soft warnings, not errors)
  • 2 `invalid-enum` errors on `confidence: "validated"` (drift caught; fix is a data-layer change, out of scope for this PR)

That's the point of a linter: surface drift, let the operator decide.

Test plan

  • `go test ./...` — full suite green
  • `golangci-lint run --timeout=2m` — 0 issues
  • 15 unit tests in `internal/saga/lint_test.go` covering every category + the --fix round-trip (re-lint clean after fix) + --scope filter, all using synthetic layers in `t.TempDir()` — no network, no global state
  • CLI smoke run on a synthetic project layer (human + JSON output)
  • CLI smoke run against the maintainer's real personal layer (48 notes)

Closes #19

mopanc and others added 2 commits May 11, 2026 19:47
New `saga lint` subcommand walks every topic in active layers and validates
against Saga Topic Spec v1.0. Closes the Level-2 conformance gate for
v1.0.0-rc.1 ("MUST implement lint covering: dangling relations, cycle
detection, type validity, sensitivity defaults" — spec §10).

Rules implemented (11 categories, all with stable JSON `category` strings
that tooling can filter on):

- `parse-error`            — frontmatter doesn't parse (exit code 2)
- `missing-field`          — required field absent (id, scope, type, title)
- `invalid-type`           — type not in spec §4 vocabulary
- `invalid-enum`           — confidence / lifecycle / provenance /
                             memory_family / operator_surface / sensitivity
                             outside the spec §5 allowed values
- `scope-mismatch`         — frontmatter scope ≠ layer meta.yml scope (§2.1)
- `unknown-operator`       — relation op not in spec §6.2 (accepted as opaque)
- `dangling-relation`      — target resolves to no topic in active layers (§6.1)
- `cycle`                  — @supersedes or @derived_from cycle (§6.3)
- `slug-mismatch`          — filename slug diverges from title-derived slug
                             AND filename slug isn't in synonyms (§1.1)
- `duplicate-id`           — two topics share an `id` (§1.1: "never reused")
- `missing-recommended`    — recommended frontmatter (currently `confidence`)
                             missing; `--fix` inserts spec §5.1 default

Flags:
- `--scope <name>`         — restrict to a single layer scope
- `--fix`                  — apply safe insertions (currently only the
                             recommended `confidence: tentative` default).
                             Never touches body or relations.
- `--format human|json`    — JSON for tooling; human for terminals

Exit codes: 0 clean / 1 findings / 2 parse errors.

15 unit tests covering every category + the --fix round-trip + --scope filter
(against synthetic layers in t.TempDir, no I/O outside the test dir).

Smoke-tested against the maintainer's real personal layer (48 notes): 0 parse
errors, 33 slug-mismatch warnings (titles evolved post-rename), 2 invalid-enum
errors (`confidence: "validated"` — drift caught, will be fixed at the data
layer, not in this PR).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mopanc mopanc merged commit b4d807a into main May 11, 2026
6 checks passed
@mopanc mopanc deleted the feat/saga-lint-spec-validator branch May 11, 2026 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

S0-7 saga lint — validates frontmatter against spec v1.0

1 participant