Help us shape the future of Go! Take the 2026 Go Developer Survey

finding

package
v0.35.0 Latest Latest
Warning

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

Go to latest
Published: Oct 31, 2026 License: MIT Imports: 3 Imported by: 7

Documentation

Overview

Package finding is skillet's shared diagnostic type: one Diagnostic that any linter or gate emits, carrying a Severity, an optional Category, an optional location Path, and a human Message. It replaces the per-tool finding types (exegesis lint's {Severity, Message}, modelith lint's four-field form) with one shape. A finding is a static diagnostic — not an adjudication hypothesis.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Sort

func Sort(ds []Diagnostic)

Sort orders diagnostics deterministically by severity, then category, then path, then message — so identical inputs always render in the same order. SeverityError sorts before SeverityWarning ("error" < "warning").

Types

type Action added in v0.16.0

type Action string

Action classifies who can close a finding. It is orthogonal to Severity, and the two must not be collapsed: Severity answers whether a finding blocks, Action answers who acts. A blocking finding may be safely automatic, and an advisory one may need a person.

const (
	// ActionAutomatic means a tool can generate the fix without asking.
	ActionAutomatic Action = "automatic"
	// ActionGuided means a tool can propose the fix but a person confirms it.
	ActionGuided Action = "guided"
	// ActionHuman means closing it needs judgment no tool here has.
	ActionHuman Action = "human"
)

The known actions. There is deliberately no constant for "unclassified" -- see Diagnostic.

func (Action) Valid added in v0.16.0

func (a Action) Valid() bool

Valid reports whether a is a known action. The zero value is not one: it is the unclassified state, so a caller asking "is this a real action" gets false for it.

type Diagnostic

type Diagnostic struct {
	Severity Severity `json:"severity"`
	Category string   `json:"category,omitempty"`
	Path     string   `json:"path,omitempty"`
	Message  string   `json:"message"`
	Action   Action   `json:"action,omitempty"`
}

Diagnostic is one finding about an artifact.

Action is optional and its zero value means *nobody classified this*, which is not the same claim as ActionHuman. That distinction is why there is no ActionUnknown constant to set: an unset field cannot be mistaken for a decision, and a named one invites being assigned deliberately. Same reason timeseries.Verdict keeps Compared separate from a zero baseline -- the absence of a judgement is not a judgement.

type Result

type Result struct {
	Diagnostics []Diagnostic `json:"diagnostics"`
	Unexamined  []Unexamined `json:"unexamined,omitempty"`
}

Result accumulates diagnostics from one or more checks.

Unexamined is separate from Diagnostics rather than a kind of diagnostic: it carries no severity, cannot block, and answers a different question -- not "what is wrong" but "what was not looked at". A consumer walking Diagnostics to decide an outcome never sees it, which is the intended structural guarantee.

func (*Result) Add

func (r *Result) Add(d *Diagnostic)

Add appends a diagnostic to the result.

func (*Result) HasBlocking

func (r *Result) HasBlocking() bool

HasBlocking reports whether any diagnostic is blocking (Severity error).

type Severity

type Severity string

Severity classifies a Diagnostic.

const (
	SeverityError   Severity = "error"
	SeverityWarning Severity = "warning"
)

Severity levels. Only SeverityError is blocking.

func (Severity) Blocking

func (s Severity) Blocking() bool

Blocking reports whether s should fail a gate. Error blocks; warning does not.

func (Severity) Valid

func (s Severity) Valid() bool

Valid reports whether s is a known severity.

type Unexamined added in v0.19.0

type Unexamined struct {
	Aspect string `json:"aspect"`
	Reason string `json:"reason"`
}

Unexamined is one aspect a check or critic did not look at, and why not.

It exists because an empty findings list is ambiguous: a critic asked for three categories of defect and returning none may mean it found none or may mean it never looked, and a gate shipping on that silence cannot tell. Naming the gap converts an unknown into a recorded one.

Three properties, each of which the field is useless without.

It is advisory, always. Nothing may block because an Unexamined entry is present: a critic that can block by declaring a gap learns to declare none, which costs the gap and the finding both. Result is shaped so that holds without anyone remembering it.

It is testimony, not a derived fact. A critic saying it did not examine something is a claim about its own behaviour, unverifiable from the outside, and worth exactly what the critic is worth. This is what separates it from a skipped check, where code decided a check did not apply and can say so mechanically; a caller must not read the two alike, and they are deliberately different types for that reason.

Reason is required, and Valid is the whole point of the type. "I did not examine X" with no why is the boilerplate a required-but-unread field fills with: it satisfies the schema and records nothing.

func (Unexamined) Valid added in v0.19.0

func (u Unexamined) Valid() bool

Valid reports whether u states both an aspect and a reason.

Requires: nothing. Ensures: false when either field is empty or only whitespace; pure.

A consumer parsing a critic's reply should reject the whole reply on an invalid entry rather than drop it, matching how a malformed finding is already handled: an unparseable answer must not advance anything on trust, and silently discarding half of one is how a reply that says nothing passes for a reply that found nothing.

Jump to

Keyboard shortcuts

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