Skip to content

Add config validation: every rule and its sentinel (GH-21) - #60

Merged
Ilyes512 merged 1 commit into
mainfrom
feat/GH-21-config-validate
Aug 9, 2026
Merged

Ilyes512 merged 1 commit into
mainfrom
feat/GH-21-config-validate

Conversation

@Ilyes512

@Ilyes512 Ilyes512 commented Aug 8, 2026

Copy link
Copy Markdown
Member

Closes #21.

Summary

  • internal/config/validate.go: every rule in the design's validation table, offline, first broken rule wins, each wrapped in its sentinel with %w so KindOf keeps naming it.
  • LoadFile now runs Validate, so a *Config reaching the planner has passed. Parse deliberately does not, which keeps the normalisation goldens able to drive fragments.
  • Both length bounds count Unicode code points (utf8.RuneCountInString), per the Spike: determine label description behaviour on length overflow #18 probe; names and colours — and both rename rules — fold case, per the Spike: confirm GitHub rejects case-variant label names in one repository #16 spike.
  • Table-driven tests, 60 cases, valid and invalid per rule, plus the measured boundaries; architecture and usage docs updated.

Notes on scope

  • Every box in the issue is ticked.
  • Wiring Validate into LoadFile is slightly beyond the issue's literal scope — without it nothing would call the rules, and the design says validation happens at config load.
  • The chained-rename check is unreachable given the other two rename rules (the middle name would have to be both configured and not). It is checked explicitly anyway so the message names the chain.
  • Colour uniqueness is global rather than per-repository, deliberately; the rationale is in a comment so it does not get "fixed".

internal/config/validate.go implements the design's validation table in
full, offline, and fails at the first broken rule with its sentinel
wrapped in %w so KindOf keeps naming it in JSON output. LoadFile runs it,
so a *Config that reaches the planner has passed every rule; Parse
deliberately does not, which is what lets the normalisation tests drive
fragments that were never meant to be whole files.

Both length bounds count Unicode code points via utf8.RuneCountInString.
len() would reject a valid 100-emoji description as though it were 400
while passing every ASCII test written to check it — the boundary cases
the #18 probe measured are in the table by name.

Names and colours are compared case-insensitively, and so are both
rename rules, per the #16 spike: a rename whose "from" is itself a
configured label is rejected, which makes a case-only rename an error
rather than a redundant entry, since step 5 converges casing drift on its
own.

Colour uniqueness is global rather than per-repository. That is
deliberately conservative and carries a comment saying so.

The emoji-only name rule strips emoji, variation selectors, zero-width
joiners, and whitespace and asks whether anything is left. Its range
table errs towards not matching: a pictograph it misses only lets an
emoji-only name through to the 422 it would have hit anyway, whereas one
it wrongly matched would reject a name GitHub accepts.
@Ilyes512
Ilyes512 merged commit c429e66 into main Aug 9, 2026
5 checks passed
@Ilyes512
Ilyes512 deleted the feat/GH-21-config-validate branch August 9, 2026 08:56
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.

validate.go: every validation rule and its sentinel

1 participant