Context

A downstream record (nucleation’s forcing-functions decision) built a backlog of more than a thousand undecided documents that luria never surfaced. Three blind spots, each measured there:

  • A reference checks that its target exists and is in force, and Proposed counts as in force. An Active claim resting on a Proposed one (49 edges there), or an Open question with an Active answer (37), was no finding. Which shapes are wrong is the record’s own statement, so no built-in check could close this for every record.
  • The pending report ranked by age and raw citation count. Nothing counted what rests on an undecided document, so the claims forty others depended on sorted beside stalled ideas.
  • Claims leaning on Superseded terms carried inactive-ok-file directives (“the definition current when this claim was written”). True, and it silenced forever the one question that mattered: does the claim still hold in the successor’s sense?

Patterns (ADR-129) already state shapes in a Cypher subset lowered to clingo (ADR-127); directives already take until <date> (ADR-095); the warning classes already have three dials (ADR-035, #307).

Decision

A reported pattern is a warning class. patterns.X.report: true makes every match a row of a class named X. The rows come from a fourth shown predicate of the same lowering, pattern_node(P, Match, I, X) — positional, so a match whose nodes are all anonymous is still a row — solved once for every reported pattern. The class is in lint.failable() and lint.mutable() for that record, so fail_on, baseline and mute take it like a built-in class. A row names the first node’s document; that document acknowledges its rows with pattern-ok: X (any scope), and an acknowledgement that answers for no match, or names no reported pattern, is reported under stale-directives. A reported pattern named like a built-in class is refused at load.

Reliance is declared, and counted in clingo. A top-level reliance: names reference fields that carry dependence; each must be a reference some scheme declares. logic/reliance.lp reads held/4, so a dependence stated on the converse side counts, and derives each document’s count of distinct transitive dependents (a document is never its own). When set, every pending row carries the count, the console and committed tables gain a column, and the order is dependents descending with age as the tiebreak. Unset, the rows, columns and order are exactly what they were. load_bearing: N (refused without reliance, and below 1) adds the load-bearing class.

An acknowledged retired premise needs a date. With reliance set, unbounded-acknowledgements names each inactive-ok directive that excuses, in document A, a citation of a document B that A depends on directly through a reliance field, where B is retired, and the directive has no until. Retired means out of force and not undecided (Proposed, Deferred): a Proposed premise is the pending report’s business, ranked by load-bearing.

Alternatives considered

  • A second matcher for findings, in Python. It would have to agree with the clingo lowering on converse sides, variable length and WHERE NOT, and would drift (DP-004). The fourth predicate costs a few lines of lowering.
  • Class names prefixed pattern:. Collision-free by construction, but fail_on: [pattern:inverted_premise] is a second spelling of a class to learn. The bare name, with collisions refused at load, reads like every other class.
  • An acknowledgement naming the other end’s code. Finer-grained, but a code in a pattern-ok: directive is read as a citation by the reference scan, which would need a fourth exemption. A pattern names no single site the way a citation does, so the first node’s document answering for its own matches is the honest granularity.
  • Reliance as citation count. That is the column the report already had, and the one that missed the backlog: a ledger citing a claim once is not a claim resting on it.
  • Counting Proposed targets as retired for the expiry check. Every acknowledged citation of a draft would need a date; the draft’s own urgency is what load-bearing measures.
  • Status quo. The downstream maintains a worklist.py prototype beside luria, and every new blind spot is a luria release away from being a finding.

Consequences

A blind spot found later is a pattern and a class, with no release. The pending report changes shape only for a record that sets reliance. luria lint reads every scheme document once more for pattern-ok directives (a substring test skips the parse in files that carry none), and solves reliance.lp only when reliance is set. A property map’s : is not allowed in a plain YAML scalar, so a match: with a property map must be quoted, which the configuration reference now shows.