Prefix: lint_.
The linter’s findings: one row per finding, plus the corrections a rule can compute for its own findings, and the rules themselves. A correction here is a suggestion an editor may offer, never a rewrite the build performs. The family speaks the linter’s vocabulary, where severity follows from the rule, which is why the rules are a relation here and severity is a column of it.
Where to start
- lint_finding
-
One lint finding per row, in the linter’s own vocabulary.
- lint_finding_fix
-
The correction a rule computed for one of its own findings: a suggestion an editor offers, never a rewrite the build performs.
How this family meets the others
store_-
1 foreign key from this family’s rows into that one.
Why the name is right
The linter’s vocabulary (lint_finding.lint_rule is LintRule.id(), and lint_rule states the set those ids are drawn from), its own family because a lint finding’s severity is a function of its rule, never a rejection kind, and because lint rules are predicates over classified facts that should be free to migrate store-native without contending for another family’s relation.
lint_finding
One lint finding per row, in the linter’s own vocabulary. The loader’s input is the suppression-filtered warning list the report is assembled from, never a pre-suppression stream, so rows here are post-suppression survivors exactly as the report and the MCP tool carry them; a loader reading an earlier stream would resurrect disabled findings on the wire. Severity is warning by the rule’s nature and stated only in the union view, since one relation holds one vocabulary. Two shipped producers mint whole-build findings with no SDL coordinate (session-state and dependency-currency advisories), which is what puts this relation on the emit-order key convention.
Primary key: (graph_name, ordinal). Foreign key: (graph_name) references store_graph (graph_name).
graph_name(CHARACTER VARYING, not null)-
the owning graph’s partition, anchored by store_graph; the leading key dimension that keeps one workspace’s graphs apart
ordinal(INTEGER, not null)-
emit order in the suppression-filtered warning list, 0-based; the key’s tie-breaker on the javac_diagnostic convention
lint_rule(CHARACTER VARYING, not null)-
LintRule.id(), the finding’s stable rule identifier; the vocabulary this family is named for
message(CHARACTER VARYING, not null)-
the finding’s rendered message; display material, never a dimension
file(CHARACTER VARYING, nullable)-
path of the finding’s SDL source, on rejection_validation_error.file’s terms; NULL on the whole-build findings that carry no location
source_line(INTEGER, nullable)-
source line of the finding’s location, 1-based; NULL where unlocated
source_column(INTEGER, nullable)-
source column of the finding’s location, 1-based; NULL where unlocated
lint_finding_fix
The correction a rule computed for one of its own findings: a suggestion an editor offers, never a rewrite the build performs. Its own relation rather than a column on lint_finding, because a fix is optional per rule and per site: a description column on the finding would be nullable by kind, and its absence would then mean both "this rule suggests nothing" and "this finding has no fix here". A row is the presence of a fix and the edits are its ordered child. Deliberately not projected onto the diagnostic view, whose every dimension is single-valued at one row per diagnostic where a fix is a list; a reader wanting one joins these two relations on the finding it is offered for.
Primary key: (graph_name, finding_ordinal). Foreign key: (graph_name, finding_ordinal) references lint_finding (graph_name, ordinal).
graph_name(CHARACTER VARYING, not null)-
the owning graph’s partition, carried through the parent finding row
finding_ordinal(INTEGER, not null)-
the fixed finding’s ordinal in lint_finding’s emit order
description(CHARACTER VARYING, not null)-
the fix’s rendered title, which an editor shows as the label of the action; display material, never a dimension
lint_finding_fix_edit
One text edit of a fix: the half-open range [start, end) becomes the replacement, on the DDL’s standing pattern for an ordered multi-valued decode. The positions are positions in the text the finding was computed against, which is what makes a fix unsafe to apply to a text that has moved since: an edit names a span rather than a declaration, so unlike a coordinate it cannot be re-anchored by resolving the declaration again, and a reader is expected to check the source’s recorded stamp against the text in hand before offering one. An insertion is the zero-width case where start equals end and a deletion the empty replacement; both are stored as written rather than flagged, being readable from the columns.
Primary key: (graph_name, finding_ordinal, position). Foreign key: (graph_name, finding_ordinal) references lint_finding_fix (graph_name, finding_ordinal).
graph_name(CHARACTER VARYING, not null)-
the owning graph’s partition, carried through the parent fix row
finding_ordinal(INTEGER, not null)-
the fixed finding’s ordinal, the parent fix row’s key
position(INTEGER, not null)-
0-based position within the fix’s own edit list; order is the minted fact, since a rule may write two edits whose spans do not run in source order
start_line(INTEGER, not null)-
line of the replaced range’s inclusive start, 1-based per the graphql-java convention the finding’s own location follows
start_column(INTEGER, not null)-
column of the replaced range’s inclusive start, 1-based on the same convention
end_line(INTEGER, not null)-
line of the replaced range’s exclusive end, 1-based; equal to the start line on an insertion, and on every edit a rule writes today, a token never spanning a line
end_column(INTEGER, not null)-
column of the replaced range’s exclusive end, 1-based; equal to the start column on an insertion
replacement(CHARACTER VARYING, not null)-
the text the range becomes, exactly as the rule wrote it; empty on a deletion, which is the honest spelling of replacing a span with nothing
lint_rule
One lint rule the generator declares: its identity, which producer mints its findings, and how severe a finding of it is. For example field-names-camel-case is an engine rule and a warning.
Primary key: (rule_id). Check: "SOURCE" IN('ENGINE', 'CLASSIFIER', 'CODEGEN', 'DERIVED'). Check: "SEVERITY" IN('ERROR', 'WARNING').
rule_id(CHARACTER VARYING, not null)-
the rule’s stable kebab-case identity, which is LintRule.id() and the only string form of a rule that crosses a wire. Not partitioned by graph: a rule exists because the generator declares it, not because a consumer’s schema does, so the same row serves every graph in the store
source(CHARACTER VARYING, not null)-
which producer mints this rule’s findings: an engine statement, a classifier verdict tagged at its emit site, a whole-build codegen advisory, or a reduction over a derived relation. The axis the engine’s completeness gate turns on, and about the producer rather than about what the finding is derived from, so a rule reading store rows is an engine rule like any other
severity(CHARACTER VARYING, not null)-
how severe a finding of this rule is. A column of the rule and not of the finding, per this family’s charter: severity follows from the rule, so two findings of one rule cannot disagree and nothing has to decide which of them is right. Every rule is a warning today, which is a fact about the rules rather than a limit of the column
lint_violation (view)
One rule broken at one written position: this rule has something to say about what this file wrote here. For example an input object named WidgetFilter draws a row of input-object-name-suffix at the position its declaration was written at.
graph_name(CHARACTER VARYING, nullable)-
the owning graph’s partition, carried from the entry the finding is about
lint_rule(CHARACTER VARYING, nullable)-
which rule this row is a violation of, spelled as LintRule.id(). Not a foreign key, a view carrying none: the arm that draws the row names the rule, so a row citing a rule nothing declares would have to be written into this file. Severity is not here at all, being a column of lint_rule, and a copy could disagree
source_name(CHARACTER VARYING, nullable)-
the file the offending thing was written in, the first part of the position that is this row’s subject
source_line(INTEGER, nullable)-
line of the offending entry, 1-based per the convention the entry stratum records
source_column(INTEGER, nullable)-
column of the same. Keyed on the position rather than on the coordinate the position sits in, because a lint finding is something an author goes to and fixes, and a coordinate assembled from several declaration sites names no single place to go. One consequence is deliberate: a name spelled wrongly at a base declaration and again at two extensions is three rows, which is three places the author has to edit
subject(CHARACTER VARYING, nullable)-
the name of the offending thing itself, which is what a reader renders the finding’s text around. Not the text: a rule’s wording is a template held beside the reader, and a view carrying rendered prose would be stating a product of its own presentation rather than of the corpus. Distinct from type_name, which is the enclosing type the consumer’s excludedTypes is matched against; for a field those differ, and matching the exclusion against the subject would let a consumer silence every field named the same way anywhere in the graph
subject_parent(CHARACTER VARYING, nullable)-
the name the offending thing was written inside, where the rule’s wording names it too, and null where it does not. Four of the twelve templates interpolate two names: a field prefixed with its type’s name, a root operation’s field, an argument of a directive and a field of an input object. Nullable rather than a second relation because it is an attribute of this row, absent exactly when the rule has nothing to say about an enclosing thing
subject_kind(CHARACTER VARYING, nullable)-
what kind of thing the subject is, drawn from graphql_element’s vocabulary, which is the specification’s coordinate grammar. Here because the rule alone does not determine the wording: types-and-fields-have-descriptions reads one way for a type and another for a root operation’s field, and no-deprecated-directive-usage reads three ways for a directive, an argument of one and a field of an input object. Rule and kind together do determine it, which is what lets a reader hold one template per pair and no branching of its own
subject_at_position(BOOLEAN, nullable)-
whether the position this row carries is the subject’s own name token. False where the declaration has a description, the parser reporting a described node at its description instead, so the characters at this position are the author’s prose and not the name. A reader offering to rewrite the name needs this and cannot derive it: every row looks alike from the outside, and a token replacement at a description edits whichever words happen to start it. Stated as the fact rather than as permission, because what a rule does with it differs: the two rename rules withhold their fix, where a rule inserting a description on the line above is unaffected by what the line contains