tag

package
v0.3.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 12, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package tag holds a board's shared tag vocabulary: the rules a tag name must obey, and the colour registry in .jaira/tags.

Colours live in one file for the whole board rather than on each ticket. A colour is a property of the tag, not of the ticket wearing it, and storing it per ticket would mean the same tag could render two ways on one board and that recolouring "ui" meant rewriting every ticket carrying it.

The file is read and written the way core/lane's order file is — plain text, one line per entry, hand-editable, no YAML — because that format is already the one this project uses for "a small fact about the board, not about a ticket", and a second mechanism for the same job would be a second thing to learn.

Index

Constants

View Source
const FileName = "tags"

FileName is the registry's name under .jaira. Not "tags.yaml" and not a directory: there is one fact per tag, and a line is the smallest thing git can merge.

Variables

View Source
var Palette = []int{
	33,
	45,
	40,
	71,
	100,
	136,
	178,
	208,
	209,
	197,
	168,
	170,
	135,
	63,
	111,
	73,
}

Palette is the set of colours a new tag can be given: sixteen ANSI-256 values chosen to stay apart from one another, and away from the colours the board already spends on meaning — accent 39, warning 214, error 203, ok 78, agentic 141 — so a tag swatch never reads as a status.

Sixteen is a budget, not a limit on how many tags a board may have: past sixteen, colours repeat (see Registry.Assign), which is a legibility cost and not a failure.

Functions

func Fallback

func Fallback(name string) int

Fallback is the colour a name gets once every palette colour is taken: a stable choice derived from the name itself, so an exhausted palette still gives one tag one colour on every machine, instead of a fresh random one per run and a file that never stops changing.

func Matches

func Matches(have []string, want string) bool

Matches reports whether a list of written tag names contains want.

Both sides are normalized before comparing, so a hand-edited "UI" still answers to "ui". The comparison is equality, not substring: a tag is a name from a closed vocabulary, and "cur" matching "security" is a wrong answer rather than a loose one. One implementation so the CLI's --tag and the board filter's tag: can never disagree about what carrying a tag means.

A want that cannot normalize matches nothing rather than erroring: a filter is a question, and the answer to a question about an impossible name is "no".

func Normalize

func Normalize(raw string) (name string, changed bool, err error)

Normalize turns a written tag name into the single form the board stores: lowercase kebab. It reports whether it had to change anything, so a caller can tell the person that "My UI" was filed as "my-ui" rather than leaving them to discover it on the ticket.

Whitespace and underscores become dashes. Anything else outside [a-z0-9-] is refused rather than dropped: silently deleting a character invents a second name for one topic, which is the exact thing a shared vocabulary exists to prevent. The surviving charset is also a plain YAML scalar by construction, so a normalized name always survives a round trip through frontmatter.

func Path

func Path(root string) string

Path is where a board keeps its tag colours.

func Suggest

func Suggest(raw string) (string, bool)

Suggest is a best-effort repair of a name Normalize refuses: a separator becomes a dash, then the usual collapse and trim. It reports false when there is no repair it can stand behind.

Dashing a separator is a repair. Dashing a letter is a deletion, and this is printed as a runnable command — "über" repaired to "ber" is a wrong tag one paste away, which is worse than no suggestion at all. So any letter or digit outside [a-z0-9] that survives ToLower withdraws the whole suggestion, and the refusal stands on its own.

What is left is exactly the silent trimming Normalize will not do — deliberately. Offered to a person as "did you mean this" it is a fix; applied behind their back it invents a second name for one subject, which is the whole failure the vocabulary exists to prevent. So the write path refuses and this only ever advises.

func ValidColour

func ValidColour(n int) bool

ValidColour reports whether n is an ANSI-256 colour.

Types

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry is .jaira/tags as it was read: every line kept verbatim beside the colours parsed out of them.

Keeping the raw lines is what makes a write safe on a file a person owns. A comment, a blank line, a line jaira cannot parse and a colour somebody hand-picked all survive a Set, because Set edits one line and leaves the slice alone.

func Load

func Load(root string) (*Registry, error)

Load reads a board's registry. An absent file is not an error: it means the board has no tag colours yet, which is the state every board starts in.

func (*Registry) Assign

func (r *Registry) Assign(name string) int

Assign picks a colour for a name the registry does not know: a random one no entry is using yet, so two tags on one board stay distinguishable. Random rather than next-in-sequence because sequence would give every board the same first three colours, and two teammates adding two tags at once would both pick the same "next" one.

Once every palette colour is taken it falls back to Fallback — a repeat, not a refusal.

func (*Registry) Colour

func (r *Registry) Colour(name string) (int, bool)

Colour returns the colour recorded for a name, and whether there is one. The lookup normalizes, like every other reader: the registry's keys are normalized at Load, and a ticket may carry a hand-written "UI" — which works as "ui" in the filters and the count, so it must wear ui's colour too, or "works everywhere" stops being true at the one place it shows.

func (*Registry) Names

func (r *Registry) Names() []string

Names lists the names the registry has a colour for, in the order the file gives them.

func (*Registry) Save

func (r *Registry) Save(root string) error

Save writes the registry back, creating .jaira and the header if this is the board's first tag colour.

The write goes through ticket.WriteAtomic — a temporary file in the same directory and a rename — so a reader never sees a half-written registry and a crash mid-write leaves the previous file intact rather than a truncated one. Atomicity is not exclusion: two writers still have to be serialised by a lock around Load→Set→Save, or the later one saves a file assembled before the earlier one's line existed. That lock is the caller's (see registerTags).

func (*Registry) Set

func (r *Registry) Set(name string, colour int)

Set records a colour for a normalized name.

A name already in the file has its own line rewritten in place, keeping any trailing comment. A new name is inserted at the position that keeps a sorted file sorted — appending instead would put every concurrent addition on the same last line, which is the one place two teammates' commits are guaranteed to conflict.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL