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.
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) HasBlocking ¶
HasBlocking reports whether any diagnostic is blocking (Severity error).
type Severity ¶
type Severity string
Severity classifies a Diagnostic.
Severity levels. Only SeverityError is blocking.
type Unexamined ¶ added in v0.19.0
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.