RFD 0054 — Set-valued, characterization-relative composition over standpoints
- State: discussion
Question
A record holds more than one rival way of characterizing the same underlying facts, and the domain genuinely permits more than one (a transaction read as a sale vs. a financing; a party read as an owner vs. a conduit). The two readings are not two values on a shared axis — they are outputs of two different deterministic theories over the same brute facts: one yields a recognized gain, the other a loan balance and an interest schedule. The honest computation carries every rival characterization through derivation independently and hands back the set of characterization-indexed results — never a single reconciled answer, because collapsing to one silently relocates the choice onto the machine.
How does Argon represent and evaluate this — holding a set of rival characterizations un-collapsed, deriving each independently, and reading a sound qualitative floor across them — and exactly which part is already in the substrate, which is a small addition, and which genuinely needs a model engine?
This RFD does not redefine the standpoint surface (§11), the federation
semantics (Standpoint/Federation.lean), the decidability ladder (§10), or the
world-assumption gates (RFD 0036). It sits on top of them and fixes the
composition primitive and its evaluation strategy.
Summary of the answer
A characterization is a standpoint — not by analogy but by identity: a
standpoint is a context carrying its own facts and rules, related by a <:
lattice, with each standpoint internally consistent (strict default) and
disagreement preserved across standpoints. That is exactly “within a fixed frame
the collapse is determinate fact-finding; across frames, hold the set.”
The substrate already does most of the work. The deterministic core needs one
small addition (a non-collapsing, standpoint-indexed read), and the modal
boundary read is ordinary stratified Datalog for the decidable fragment. A
model engine (tableau over std::kripke) is genuinely required, but only
for the intractable / infinite / unstratified fragment, and a refusal gate holds
its place soundly until it lands.
Verified ground truth
Every claim below is read from source; the design rests on these.
-
Federation does not collapse rival values.
Store::query_derive_federatedkeys the AFT info-join on the full tuple (encode_tuple), so distinct per-characterization values are distinct tuples that each survive asIs. What federation discards is the standpoint index (it folds over(_standpoint, rows)).Truth4::Botharises only for the identical tuple asserted positively in one standpoint and negatively in another (Federation.lean::federate_eq_both_iff;AFTEquivalence.leanT3:both ⟺ no consistent global K3-section). -
Standpoints are the characterization carrier. A standpoint scopes facts and derive rules (§11). Each standpoint is internally K3-consistent under the default
strictconsistency policy; cross-standpoint disagreement is preserved at federation regardless of policy (Consistency.lean). The<:lattice is an enforced DAG (OE1211), so the cross-standpoint fold always stratifies stratum-by-stratum (Stratification.lean, Vennekens-Gilis-Denecker 2006). The per-standpoint evaluation context is materialized today bymaterialize_predicates_for_standpoint(DEFAULT layer ∪ stamped-sfacts; the runtime side ofVisibility.scoped_view_eq_default_union_own). -
Modal over the standpoint frame is finite ∀/∃, and compiles to Datalog.
box w P = ∀w'. accessible w w' → P w'; the standpoint frame is S4, worlds = standpoints, accessibility = the<:reflexive-transitive closure (Reasoning/Modal.lean). Gómez-Álvarez, Rudolph & Straß (KR 2023) prove Standpoint-EL+ compiles to ~30 pure Datalog rules (PTIME), worlds reified as standpoint IDs, accessibility as explicit transitive closure, withboxvia stratified negation or count-equality. The<:ancestor closure is already precomputed (standpoint_lattice.rs::ancestors,global-control.standpoint_ancestor_sets). -
The intractable fragment is sharp. Adding empty standpoints (NP-hard), rigid roles (coNP-hard), or nominals (EXPTIME-hard) each independently breaks PTIME (IJCAI 2023); modal⊗temporal cross-nesting is already refused (
OE0712). These, plus the rigidity frame over infinite configuration worlds and unstratified modal recursion, are the fragment that needs a model engine. -
There is no single “modal engine” — evaluation is a compile-time strategy choice (translation to Datalog vs. tableau-on-demand). The decidability ladder places
modalas its own tier (cost modal+FOL) that dispatches: static discharge, else an external reasoner overstd::kripke(Decidability/Modal.lean). -
Two gaps are live in the implementation. (a)
AtomIR::Modal{op,inner}is frame-blind and is stripped toinnerat compile (compile/rule.rs), while being classifiedTier::Modaland admitted — so abox/diamondprogram builds, runs, and is confidently wrong for anything but the rigid-default case (a live silent-wrong). (b)std::kripkeis not built (no package), and there is no standpoint-frame discharge in the Lean (only the rigidity-framefixeddischarge inStaticDischarge.lean).
Design
D1 — The reification surface: the across binder
The across clause on a query gains an optional binder:
federation-clause ::= 'across' (Ident 'in')? '[' standpoint-ref (',' …)* ']'
across [S₁, S₂](no binder) — federate: the existing AFT info-join. Preserved unchanged; it is the right tool for genuine same-proposition disagreement detection (theBothread).across s in [S₁, S₂](binder) — enumerate: bindsto each standpoint in turn, evaluate the body in standpoints’s view, and projectsas a first-classStandpoint-typed column. The result is the rival-set, indexed by characterization — held un-collapsed.
The binder/no-binder distinction is the enumerate/federate switch: local,
syntactic, no new keyword. The runtime already computes the per-standpoint
contributions (materialize_predicates_for_standpoint per sid, then the
dispatcher); enumerate keeps the sid as a result column instead of folding it
away. This is the product/relational Kripke-frame strategy (“add a world
variable to every predicate”) realized at the query boundary.
A Standpoint value is a reflective name (the sid: NameRef the runtime
already carries), in the family of the reflective TypeRef (RFD 0027 / the
reflective-name surface). It is an ordinary value: downstream rules join on it.
D2 — The boundary read: qualitative □ / ◇, never counted
With the standpoint reified as a column on a relation R(s, …), the
characterization-relative reads are ordinary, already-wired aggregates:
□(necessity / sound floor) —forall s in frames: R(s, …)— survives under every admissible characterization.foralllowers to count-equality (count{s : P} == count{s : frame}), which is a boolean, not a ratio.◇(possibility / contestable frontier) —exists s in frames: R(s, …).- qualitative worst-case —
◇(¬survives): is there an admissible frame under which it fails (the adversary’s move). Wired via NAF + exists.
The discipline is structural: the natural encoding produces a qualitative
verdict, never a measure. A count of frames (count{surviving}/count{frames}
→ a probability over worlds) is an off-path, deliberate construction — the
forbidden scalarization, and not what □/◇ produce. The sound floor is a
meet computed as a read that leaves the held set intact; it is never the AFT
info-join (which would replace the set with one tag). The numeric min/
argmin over a value codomain is not a substrate read: it is the optimizer’s
move on the held set, human-invoked, and lives above the language.
Soundness of □-as-forall over the frame set rests on the frame set being
closed and complete — which it is: the across set together with its <:
ancestors is fully known (the closed-world-over-a-closed-set condition the
federation gates already enforce, §6.9 / RFD 0036).
D3 — Standpoint-frame box / diamond translation
For box/diamond appearing in a rule body over the standpoint frame
(frame root recovered from the enclosing standpoint block via the lowering
context, or the across set for a query — see D7), lowering translates to
stratified Datalog over the reified relations and the standpoint accessibility
relation reach:
diamond_s P(x̄) ≡ ∃ s'. reach(s, s') ∧ P(s', x̄)
box_s P(x̄) ≡ ¬ ∃ s'. reach(s, s') ∧ ¬ P(s', x̄) (stratified NAF)
reach(s, s') is the <: reflexive-transitive closure (the precomputed
ancestor sets), exposed as a reasoning relation. P(s', x̄) is P reified with
the world column — materialized per standpoint and tagged (the product
reification). Tiers: closure (the <: closure) + recursive (the stratified
aggregate). This is the KR-2023 reduction; the box-as-NAF form is sound
because the frame set is closed (D2).
D4 — The dispatch and the silent-wrong fix
box/diamond lowering becomes frame-aware, replacing the unconditional
strip:
| Atom | Frame / target | Action |
|---|---|---|
box/diamond(x : T), T fixed/rigid | rigidity (classification) | discharge to x : T (the existing sound reduction, now principled) — StaticDischarge.box_fixed_discharge |
box/diamond(P), explicit standpoint frame, decidable | standpoint | translate (D3) |
box(¬(x:T)), dynamic/anti-rigid rigidity frame, empty standpoints, rigid roles, nominals, unstratified modal, modal⊗temporal | needs tableau | refuse — new OE11xx ModalNotEvaluated, mirroring the bridge OE1102 discipline |
This closes the live silent-wrong: a program that the engine cannot soundly evaluate refuses loudly rather than silently stripping to rigid-default.
D5 — Determinacy-mode is a user enum
The tripartite determinacy-mode {indeterminate-by-law | unknown-by-data | low-confidence} is a flat user-level enum column on the per-characterization
facts, carried through derivation and dispatched on by the D2 aggregates (e.g.
□(survives ∧ mode ≠ indeterminate-by-law)). It is not a truth-value
algebra: Truth4 is not extended, and the determinacy-mode is orthogonal to
both Truth4 (cross-standpoint agreement) and well-founded-undefined
(derivability). Argon’s enum values (struct/enum value carriers) already model
it; this is near-commodity and needs no substrate change.
D6 — The model engine (held by the refusal gate)
The non-translatable fragment (D4 “refuse” row) is served by a tableau model
engine over a built std::kripke carrier (World, Entity, iof, accessible, current). This is the genuine tier:modal backend. It is the one large build;
the OE11xx refusal makes deferring it sound (loud refusal, never silent
wrong). It is not “purely mechanical mechanization of well-understood math,” so
it carries its own design and (when built) its own Lean obligations — it is out
of scope for the Lean deferral that covers D3’s finite-frame reduction.
What does not change
Truth4stays four-valued.Bothis genuine same-proposition contradiction (= no consistent global section), orthogonal to rival values.- Federation default stays
Paraconsistent(no-binderacross). Enumerate is the new, additive, lossless peer; federation remains the lossy opt-in for disagreement detection. - The
<:DAG, the consistency policies, the world-assumption gates are untouched.
Decidability placement
- D1 enumerate + D2 reads + the decidable D3 translation:
tier:closure(<:closure) +tier:recursive(stratified aggregates / NAF). Executable on the existing semi-naive engine. - D4 “refuse” / D6:
tier:modal. Refused until the engine lands.
Implementation plan (dependency-sorted)
- Phase A — the unblocking core. D1 binder (grammar + lowering + the
enumerate readout keeping
sid) + theStandpoint-typed column + D2 reads as ordinaryforall/exists. End-to-end testable; delivers hold-the-rival-set + the qualitative floor with no engine. - Phase B — soundness + mid-body. D4 frame-aware dispatch (discharge / refuse,
killing the silent-wrong) + D3 standpoint-frame translation (
reachrelation + thebox/diamondlowering). Requires D7 below. - Phase C — the escape hatch. D6 tableau engine +
std::kripkepackage.
D7 — Prerequisites surfaced by verification
AtomIR::Modalframe recovery: determine the frame at lowering (wherectx.standpoint_id/ theacrossset / “isinnera classifier atom” are available) and emit translated/discharged ordinaryAtomIR, soAtomIR::Modalsurvives only for the tableau-bound case. This avoids a wire/IR shape change.- Query lowering order: thread the
acrossset into the lowering context before body lowering (today it is extracted after). Derives already carry the enclosing standpoint viactx.standpoint_id. - Expose
reach(s, s')(the precomputed<:ancestor closure) as a reasoning relation for D3.
Lean note
Per a one-time, explicitly-authorized deferral (this primitive is blocking a
dependent project and D3’s reduction is the proven KR-2023 finite-frame result,
not novel mathematics): the soundness proof of the D3 standpoint-frame
discharge is deferred. The data-shape mirror that the oxc-protocol drift
gate enforces is not deferred — any IR change stays in lockstep with the Lean
@[language_interface] inductives. D6 (the model engine) is out of this
deferral.
References
- Verified sources:
spec/lean/Argon/Reasoning/{Modal,StaticDischarge}.lean,spec/lean/Argon/Decidability/Modal.lean,spec/lean/Argon/Standpoint/{Federation,Consistency,AFTEquivalence,Stratification}.lean,compiler/crates/oxc-runtime/src/lib.rs(query_derive_federated,materialize_predicates_for_standpoint),compiler/crates/oxc-oxbin/src/sections/standpoint_lattice.rs,spec/reference/src/semantics/decidability.md,spec/reference/src/semantics/standpoints-and-federation.md. - Gómez-Álvarez & Rudolph (2021), Standpoint Logic (JELIA): polytime reduction to OWL 2 by reification.
- Gómez-Álvarez, Rudolph & Straß (2023), A Datalog reasoner for Standpoint-EL+ (KR): ~30 Datalog rules, PTIME; intractability boundaries (IJCAI 2023).
- Vennekens, Gilis & Denecker (2006), Splitting an Operator: stratified fixpoint over a DAG of strata.
- Belnap (1977); Denecker, Marek, Truszczyński (2000), Approximation Fixpoint Theory.