Skip to main content
Version: v0.9.0a2
Spec

Spec-20-Lint-Semantics

5 min readSpec contributor · Adapter authorPOST /v1/lint

What this page is

Rendered compatibility entry point for Spec-20-Lint-Semantics. The lint operation is a first-class Stigmem protocol operation that performs health-check sweeps over a bounded scope or entity.

Lint is strictly read-only.

It observes and reports issues without writing facts or modifying node state. Lint bridges the decay engine (§15) and the current production node — running lint_scope against a live node reveals knowledge-base health degradation before it affects query results or agent behavior.

Authoritative source: spec/stigmem-spec-v0.9.0a1.md

Section body

Each subsection below shows the most recent normative text from the spec source.

§14.1 Lint checks

Four normative checks, each independently selectable.

Check
Class
What it detects
contradiction
correctness
Facts sharing the same (entity, relation, scope) tuple where both have confidence > 0.0 and the conflict is unresolved (status "unresolved" in the conflicts table).
stale
freshness
Facts whose valid_until < now and confidence > 0.0; optionally, facts whose valid_until < now + stale_lookahead_s (approaching expiry).
orphan
coverage
Entities where every known fact is either retracted (confidence = 0.0) or expired (valid_until < now). No live facts remain for the entity.
broken_ref
integrity
Facts with value.type = "ref" whose value.v targets an entity or fact ID that has no live (non-retracted, non-expired) facts on this node.

Default behavior: If checks is omitted or empty, all four checks run.

Scope of search: Each check operates within the scope specified in the lint request. Checks never cross scope boundaries — a broken_ref finding in company scope is only reported if the broken ref also falls within company scope.

§14.2 LintFinding shape

LintFinding {
check: "contradiction" | "stale" | "orphan" | "broken_ref"
severity: "error" | "warning" | "info"
entity: URI // entity under examination
relation: string | null // relevant relation; null for the orphan check
fact_ids: UUID[] // IDs of the fact(s) involved in the finding
detail: string // human-readable explanation suitable for display
}

§14.3 Severity mapping

Check · Condition
Severity
Rationale
contradiction · unresolved conflict between live facts
error
Unresolved contradictions corrupt query results for all callers.
stale · valid_until < now
warning
Non-critical health signal.
stale · within stale_lookahead_s
info
Approaching expiry but not yet expired.
orphan · entity has no live facts
info
Expired or empty entities do not block reads.
broken_ref · target has no live facts
warning
Standard broken-ref.
broken_ref · on intent:handoff_to or intent:context_ref
error
A broken handoff silently discards agent context during delegation.

§14.4 Wire format

Request

POST /v1/lint
Authorization: Bearer <api-key>
Content-Type: application/json

{
"scope": FactScope, // required: which scope to sweep
"checks": string[], // optional; default: ["contradiction","stale","orphan","broken_ref"]
"entity": URI?, // optional: restrict sweep to one entity
"relation": string?, // optional: restrict sweep to one relation
"stale_lookahead_s": integer? // optional: also flag facts expiring within N seconds; default 0
}

Response

200 OK
{
"findings": LintFinding[], // zero or more findings; empty array = clean
"checked_at": string, // ISO 8601 UTC timestamp of when the sweep ran
"scope": FactScope, // echoed from the request
"checks_run": string[], // which checks actually ran (echoed or defaulted)
"fact_count": integer // number of facts scanned (not findings)
}

Authorization

The caller's API key must have read access to the requested scope (§3.5). Nodes MUST return HTTP 403 if the key's allowed_scopes does not include the requested scope.

Error responses

HTTP
Class
Condition
400
bad request
scope field missing or invalid; unknown checks value.
403
forbidden
Caller's key is not authorized for the requested scope.
202
async
Scope exceeds 100,000 facts (async path; see §14.5).

§14.5 Performance contract

Sync ≤ 30s for <100k facts

POST /v1/lint MUST respond synchronously within 30 seconds for scopes with fewer than 100,000 facts.

Async for larger scopes

For scopes exceeding 100,000 facts, nodes MAY respond with HTTP 202 and a job_id. The caller polls GET /v1/lint/jobs/:job_id. Async job API is specified but deferred to the pre-reset substrate work.

Read-only invariant

The sweep MUST be read-only. Nodes MUST NOT assert, retract, or update any fact as a side effect of a lint call. This invariant applies even to internal bookkeeping.

202 Accepted
{ "job_id": "<uuid>", "status": "pending", "estimated_s": integer }

§14.6 Relationship to other operations

Lint is diagnostic, not prescriptive.

Finding type
Lint reports
Remediation action (separate call)
contradiction
which facts conflict
POST /v1/conflicts/:id/resolve (§5.10).
stale
which facts have expired
POST /v1/decay/sweep with mode="retract" (§15) or POST /v1/facts with confidence=0.0.
orphan
which entities have no live facts
No action required; orphans are informational.
broken_ref
which ref facts have missing targets
Assert missing target entity, or retract the broken ref.

§14.7 MCP tool: lint_scope

The lint_scope MCP tool exposes POST /v1/lint to any MCP-aware agent without SDK installation.

Tool definition

{
"name": "lint_scope",
"description": "Sweep a Stigmem scope for knowledge-base health issues. Checks for: unresolved contradictions, stale or expiring facts, orphaned entities with no live facts, and broken cross-references. Read-only — reports findings without modifying any facts. Use resolve_contradiction to fix contradictions found here.",
"inputSchema": {
"type": "object",
"properties": {
"scope": {
"type": "string",
"enum": ["local", "team", "company", "public"],
"description": "The fact scope to sweep."
},
"checks": {
"type": "array",
"items": {
"type": "string",
"enum": ["contradiction", "stale", "orphan", "broken_ref"]
},
"description": "Which checks to run. Omit to run all four."
},
"entity": {
"type": "string",
"description": "Optional. Restrict sweep to facts about a single entity URI."
},
"relation": {
"type": "string",
"description": "Optional. Restrict sweep to a single relation."
},
"stale_lookahead_s": {
"type": "integer",
"description": "Optional. Also flag facts expiring within this many seconds. Default 0 (expired-only)."
}
},
"required": ["scope"]
}
}

Output shape

{
"findings": [
{
"check": "contradiction",
"severity": "error",
"entity": "stigmem://company.example/user/alice",
"relation": "memory:role",
"fact_ids": ["fact-uuid-1", "fact-uuid-2"],
"detail": "Two live facts with different values for (entity, relation, scope)"
}
],
"checked_at": "2026-05-02T14:00:00Z",
"scope": "company",
"checks_run": ["contradiction", "stale", "orphan", "broken_ref"],
"fact_count": 1024
}

§14.8 Conformance test vectors

Normative lint vectors are defined in sdks/stigmem-py/tests/conformance_vectors.py under LINT_VECTORS. Each vector includes a setup list of fact assertions to run before the lint sweep, so results are deterministic.

Required vectors for conformance — all eight MUST pass against a reference Stigmem node.

Vector ID
Check
Scenario · expected findings
lint-contradiction
contradiction
Two facts same (entity, relation, scope), different values, both confidence > 0 → ≥1 finding, check=contradiction, severity=error.
lint-stale
stale
Fact with valid_until in the past → ≥1 finding, check=stale, severity=warning.
lint-stale-lookahead
stale
Fact with valid_until within lookahead window but not yet elapsed → ≥1 finding, severity=info.
lint-orphan
orphan
Entity with only retracted facts → ≥1 finding, severity=info.
lint-broken-ref
broken_ref
Ref fact pointing to entity with no live facts → ≥1 finding, severity=warning.
lint-broken-ref-intent
broken_ref
Broken ref on intent:handoff_to relation → ≥1 finding, severity=error.
lint-clean
all
Scope with only one healthy live fact → findings = [].
lint-scope-filter
contradiction
Contradiction in company scope; lint request on local scope → findings = [] (scope isolation).