The public API, and where it stops
From 1.0, some of what SQLens exposes is a promise and the rest is implementation. This page draws that line explicitly, because a boundary implied by omission is not a boundary: the next person to depend on an internal class has no way to know they did.
It gathers what is already written elsewhere and links rather than restates it. Three contract documents that disagree are worse than one that is incomplete, so each promise has exactly one source and this page points at it.
The four promises
These are public API. Renaming or repurposing any of them is a breaking change.
| Promise | What it covers | Written in full |
|---|---|---|
| Rule ids | PG.L2.INDEX_NOT_CONCURRENT and its kind | Conventions |
| Message prefixes | the namespace every finding's identifier carries, one per suite | Conventions |
| Exit codes | the four values and their meanings | The public contracts |
| The baseline format | the file a repository commits, including its schema version | The public contracts |
The message prefixes, in full:
sqlens.lint sqlens.audit sqlens.security sqlens.deploy sqlens.analyse
They are listed in a block rather than inline, and that is not formatting: a prefix written as an
inline code span is indistinguishable from a CONFIG PATH — same shape, same leading sqlens. — and
the guard that keeps this portal from citing configuration keys the schema does not declare reads
every such span as one. The block says the same thing and cannot be misread by either a person or
the guard.
A rule is never deleted. When one is superseded it becomes deprecated: it stops producing findings and says so, naming what replaced it, and a suppression that names it keeps working.
Also public, and easy to overlook
The four above are the ones a breaking change is measured against. Two more are public in the sense that something outside this package reads them:
- The rule contract — the interfaces a third-party rule package implements. It is the extension point the ecosystem is built on, so changing its shape breaks packages this repository cannot see.
- The JSON and SARIF report schema — including
schema_versionand the policy for raising it. See the JSON envelope and the schema-version policy.
The remediation payload is preview, and that is a promise too
The machine-readable fix material every safety finding can carry is not yet on the list above.
It ships marked preview, and the marking is in the payload itself rather than only here — every
payload carries its own schema_version and stability, so a consumer reading one never has to
find this page to know what it may rely on.
What preview buys, in both directions:
- For us: the shape may still move within a minor release. It is a young format, agents are the first consumers, and freezing it before anybody had built against it would have guaranteed freezing the wrong thing.
- For you: pin the version you read. A payload announces its own, so code that checks it before trusting the fields around it keeps working across a bump — and code that does not will break on one.
When the shape settles it is promoted deliberately, and after that a version bump costs a major
release like any other promised format. The two states are not a matter of intent: the machine-
readable surface carries remediation_schema_stability, and the breaking-change check reads it to
decide which rule applies to a version change.
Explicitly NOT public
Everything below the contracts is implementation, and it moves in any release:
- Concrete classes under the contracts — a rule class, a reporter, a formatter. The interface is the promise; the class behind it is not.
- The catalog readers — what they query, how they batch it, the shape of a reading. They exist to answer rules, and their signatures follow whatever the rules need.
- Canonicalization internals — how a statement is normalized before it is compared. The result is stable enough for the determinism guarantee; the path to it is not.
- Everything under
Support,Engineand the driver namespaces.
Depending on any of these is allowed — it is ordinary PHP — but it is not covered, and a minor release may move it without a changelog entry.
Commands: the rule that decides
A command registered in the regular service provider appears in artisan list in every
application that installs this package, and is therefore part of the promise. A developer command
is registered exclusively through a development/test service provider, and is not.
That sentence is the whole rule, and it is written this way round on purpose: membership follows
from where the command is registered, not from what it is called or who meant it for whom. A
tool nobody intended as public still becomes public the moment a composer require puts it in
somebody's artisan list.
The commands covered by the promise today:
sqlens:agent-rules sqlens:audit sqlens:baseline sqlens:doctor
sqlens:drift sqlens:format sqlens:lint sqlens:mcp
sqlens:postdeploy sqlens:predeploy sqlens:security
This list is held to the booted package by a test, in both directions: a command registered in the regular provider that is missing here fails, and a command named here that the package does not register fails too. The second direction caught its first error on the day it was written — this list originally carried a tenth name that appears in a source comment as a planned command and is registered nowhere.
The promise is checked, not trusted
A versioning policy that only exists in prose is a policy that drifts. A release gate diffs the rule contract against the last released one and refuses a raised severity whose changelog callout is missing — along with any lowered severity, any deleted rule id, and any change it cannot classify.
The one deliberate exception to "new behavior waits for a major": a security severity may be raised in a minor, with a callout in the changelog. Waiting for a major to say a finding is worse than first judged protects the release schedule at the expense of the person running it.
Stability tiers, the deprecation policy and the security exception are stated in full under stability tiers — they are quoted here rather than redefined.