A working-RDF rebuttal of the Symbol Grounding Framework thesis: named graphs, RDF-star, PROV-O, OWL2 entailment, SHACL, and Virtuoso/FuXi reasoning answer all twelve alleged triple-grain failures.
James Stakelum's "Why the Triple Is the Wrong Grain" argues the RDF/OWL triple is architecturally unfit for machine-to-machine AI communication — this page answers that claim failure by failure, with working RDF, not just counter-argument.
The article catalogues twelve failures across event structure, provenance, epistemic status, grounding, relational modeling, and cross-system exchange, then proposes the Symbol Grounding Framework (SGF) and its "Synapse" hub-and-spoke claim structure as a replacement grain.
Each failure is met with a standards-based RDF correction already in production use: n-ary event modeling, named-graph provenance, PROV-O attribution, RDF-star (RDF 1.2) quoted triples for saying-versus-said, SHACL-enforced purpose properties, and deterministic OWL2 entailment — via Virtuoso's built-in reasoner or a FuXi N3 rule — in place of embedding-similarity alignment.
The recurring pattern: every failure traces to an under-specified illustrative triple in the article, not a structural ceiling on the triple as a data model — the capabilities SGF proposes as new primitives already have named, standardized RDF equivalents. A second, independent objection from John F. Sowa gets the same treatment below. See Rebuttal for the full failure-by-failure breakdown with corrected Turtle and SPARQL for each, and Also Answering for Sowa's four claims.
Each of the article's twelve alleged triple-grain failures, paired one-for-one with a working RDF/SPARQL correction — named graphs, RDF-star (RDF 1.2) quoted triples, PROV-O provenance, SHACL validation, and deterministic OWL2 entailment via Virtuoso's built-in reasoner or FuXi N3 forward-chaining rules. Real instance data for these corrections and eleven live SPARQL queries against it are in the Demo Instance Data section below and the footer SPARQL workbench.
“Beethoven composed the Ninth Symphony in Vienna in 1824”
Objection: The verb becomes merely an edge label without structural boundaries; an event decomposed into separate (subject, predicate, object) triples loses its identity as a single whole.
The event is not the triple — it is a first-class resource that a set of triples describe, exactly as PROV-O and schema.org model events today. Retrieval targets the event IRI or its named graph, not any single edge, so nothing is 'shattered'; the verb is not an edge label, it is the rdf:type of the event resource.
GRAPH <urn:g:beethoven-1824-composition> {
:beethovenNinthComposition a schema:CreateAction ;
schema:agent :Beethoven ;
schema:result :NinthSymphony ;
schema:location :Vienna ;
schema:startTime "1824"^^xsd:gYear .
}CONSTRUCT { ?event ?p ?o }
FROM NAMED <urn:g:beethoven-1824-composition>
WHERE { GRAPH <urn:g:beethoven-1824-composition> { ?event ?p ?o } }Reasoning mechanism: Virtuoso quad-scoped GRAPH retrieval: the event IRI, not the individual triple, is the unit of retrieval and identity, so the whole event round-trips atomically from a single CONSTRUCT.
Illustrative named graph: urn:g:beethoven-1824-composition
“:Beethoven :composed :NinthSymphony — identical whether sourced from a scholarly biography or a random comment”
Objection: The identical triple `:Beethoven :composed :NinthSymphony` carries no structural requirement to record whether it came from a scholarly biography or a random comment; named graphs are an optional bolt-on that can be stripped.
In any RDF quad store (Virtuoso included), every triple is physically stored as (subject, predicate, object, graph) — the graph column is not optional metadata, it is the storage unit. PROV-O then gives that graph IRI a typed, queryable attribution chain.
GRAPH <urn:g:src-scholarly-biography> {
:Beethoven :composed :NinthSymphony .
}
<urn:g:src-scholarly-biography> a prov:Entity ;
prov:wasAttributedTo :scholarlyBiographySource .PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
PREFIX prov: <http://www.w3.org/ns/prov#>
SELECT DISTINCT ?g ?attribution
FROM NAMED <urn:g:src-scholarly-biography>
WHERE {
GRAPH ?g { :Beethoven :composed :NinthSymphony }
?g prov:wasAttributedTo ?attribution
}Reasoning mechanism: Virtuoso stores every triple as a quad; provenance is not an RDF-model add-on but the fourth, structurally mandatory column that PROV-O annotates.
Illustrative named graph: urn:g:src-scholarly-biography
“Beethoven was born in Bonn (documented fact) versus Beethoven was born in 1770 (inferred from baptismal records) — identical triples despite different epistemic grounding”
Objection: Fact, inference, and guess are indistinguishable at the triple level: a documented fact and an inference from indirect evidence produce identical triples.
Fact, inference, and guess are separated by placing each claim in an epistemic-tier named graph, then using a forward-chaining rule to propagate the tier onto every triple in that graph — exactly the kind of finite, deterministic derivation the article demands, running on RDF's own reasoning stack.
GRAPH <urn:g:epistemic:documented-fact> { :Beethoven :birthPlace :Bonn }
GRAPH <urn:g:epistemic:inferred> { :Beethoven :birthYear "1770"^^xsd:gYear }
<urn:g:epistemic:inferred> a :InferredClaimGraph .# N3 rule (not SPARQL) — for FuXi or another N3-aware forward-chaining reasoner
@prefix : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#> .
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
{ ?g a :InferredClaimGraph . ?g log:semantics ?f . ?f log:includes { ?s ?p ?o } } => { ?s :epistemicStatus :Inferred } .Reasoning mechanism: FuXi N3 forward-chaining rule promotes the graph-level epistemic tier to a per-triple :epistemicStatus annotation; the equivalent materialization also runs as a plain SPARQL INSERT ... WHERE over named graphs in Virtuoso.
Illustrative named graph: urn:g:epistemic:inferred
“The URI :Beethoven cannot disambiguate composer from dog from crater without external ontology”
Objection: A URI like `:Beethoven` cannot disambiguate composer from dog from crater without reliance on an external ontology, causing dangling references.
A document-local URI is exactly analogous to SGF's own 'Canonical ID with finite IS-A path to a Prime Registry' — it is deliberately under-specified until linked. owl:sameAs to a DBpedia/Wikidata IRI supplies the disambiguating IS-A path, and OWL2 reasoners close that identity transitively and deterministically.
:Beethoven owl:sameAs <http://dbpedia.org/resource/Ludwig_van_Beethoven> .PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
# (1) Baseline — no reasoning. Empirically verified against the live demo
# dataset: returns false, because :Beethoven carries no direct rdf:type
# triple to dbo:Person, only owl:sameAs to the DBpedia resource.
ASK { :Beethoven a <http://dbpedia.org/ontology/Person> }
# (2) Same ASK with Virtuoso's same-as reasoning pragma enabled — no named
# ruleset needed, just the pragma. Empirically verified: returns true.
# Deliberately has NO FROM/GRAPH restriction: the entailment bridges our
# locally-asserted owl:sameAs triple with DBpedia's OWN rdf:type
# assertion, which lives in a different graph elsewhere in the store —
# scoping to our own graph would exclude that assertion and defeat the
# demonstration.
DEFINE input:same-as "yes"
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
ASK { :Beethoven a <http://dbpedia.org/ontology/Person> }
# (3) Equivalent entailment as a portable FuXi N3 rule (works on any N3-aware
# reasoner, not just Virtuoso's pragma):
@prefix : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
{ ?x owl:sameAs ?y . ?y a ?C } => { ?x a ?C } .
{ ?x owl:sameAs ?y . ?x a ?C } => { ?y a ?C } .Reasoning mechanism: Virtuoso's input:same-as query pragma (no named ruleset required) closes owl:sameAs transitively across the identity graph on demand: without it the ASK is false (no direct rdf:type assertion), with it the identical ASK is true (propagated via the owl:sameAs link to DBpedia's own rdf:type assertion) — empirically verified against the live demo dataset both ways. A FuXi N3 rule computes the identical entailment without any Virtuoso-specific pragma, for portability to any N3-aware reasoner.
Illustrative named graph: urn:g:identity:beethoven
“"Scalpel" defined as cutting_tool with blade and handle lacks the purpose: cut living tissue with precision”
Objection: A class definition built from structural parts (e.g. blade + handle) cannot capture the function or purpose the object serves.
RDFS/OWL classes are not limited to structural parts; a purpose/function datatype property is ordinary RDF. What the article's example actually shows is an under-specified class definition, correctable with a SHACL shape that makes the purpose triple mandatory for every instance of the class.
:Scalpel a :SurgicalTool ;
schema:description "Cutting tool for making precise incisions in living tissue."@en ;
:hasPurpose "cut living tissue with precision"@en .
:SurgicalToolShape a sh:NodeShape ;
sh:targetClass :SurgicalTool ;
sh:property [ sh:path :hasPurpose ; sh:minCount 1 ] .PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
SELECT DISTINCT ?tool WHERE { ?tool a :SurgicalTool . FILTER NOT EXISTS { ?tool :hasPurpose ?p } }Reasoning mechanism: Virtuoso SHACL validation rejects any :SurgicalTool instance lacking a :hasPurpose triple, enforcing function-capture at write time rather than leaving it optional.
Illustrative named graph: urn:g:tbox:surgical-tools
“The defendant said "I am innocent" — the system cannot distinguish belief in innocence from recording the utterance”
Objection: A triple recording an utterance is indistinguishable from a triple asserting the utterance's content is true.
RDF 1.2's quoted-triple syntax lets an entire triple be the subject of a further triple, so 'the defendant said X' and 'X is true' are structurally distinct statements about a statement — exactly the saying-versus-said distinction the article claims is impossible.
<<:defendant :claims "I am innocent"@en>> :speechAct :INFORM ; :assertedBy :courtTranscript .
:defendantInnocenceUtterance a rdf:Statement ;
rdf:subject :defendant ; rdf:predicate :claims ; rdf:object "I am innocent"@en ;
:speechAct :INFORM ; :assertedBy :courtTranscript .PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT DISTINCT ?claim ?speechAct WHERE {
?claim a rdf:Statement ;
rdf:subject :defendant ; rdf:predicate :claims ; rdf:object "I am innocent"@en ;
:speechAct ?speechAct .
}Reasoning mechanism: Classic rdf:Statement reification stores the utterance as an addressable node, so belief-about-the-utterance and the utterance itself are two different, independently queryable resources — the same distinction RDF-star's <<...>> syntax expresses more compactly, and already does on Virtuoso Open Source Edition. Verified live (2026-08-13) against this document's own demo dataset using reification, since the commercial edition behind the live URIBurner endpoint this document links to will support these RDF release enhancements in an upcoming release.
Illustrative named graph: urn:g:testimony:defendant-statement
“Source A claims Bonn as birthplace; Source B claims Koblenz — the "average" would create a hallucinated third location neither supports”
Objection: When two sources disagree, naive merging produces a synthesized truth that neither source actually supports.
No conformant SPARQL engine merges Bonn and Koblenz into a hallucinated third city; each claim lives in its own source-attributed named graph, and a GRAPH-scoped SELECT returns both claims labeled by source. Reconciliation, if desired, is an explicit, auditable CONSTRUCT rule chosen by the application — never an implicit average.
GRAPH <urn:g:sourceA> { :Beethoven :birthPlace :Bonn }
GRAPH <urn:g:sourceB> { :Beethoven :birthPlace :Koblenz }PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
SELECT DISTINCT ?g ?place
FROM NAMED <urn:g:sourceA>
FROM NAMED <urn:g:sourceB>
WHERE { GRAPH ?g { :Beethoven :birthPlace ?place } }Reasoning mechanism: Virtuoso's GRAPH-scoped SPARQL SELECT returns each conflicting claim tagged with its source graph; no averaging step exists anywhere in the SPARQL 1.1 algebra.
Illustrative named graph: urn:g:sourceA
“The king died. He had eaten poison the night before — poison chronologically precedes death but narrative presents death first”
Objection: A triple store cannot represent that events are recounted in an order different from the order they actually occurred.
The premise misdiagnoses the model: RDF makes no claim about document/narrative order at all, so there is no narrative-vs-chronology collapse to fix. Chronology is asserted explicitly via xsd:dateTime literals and an explicit :precedes/prov:startedAtTime ordering, entirely independent of how or in what order the triples are written or read.
:precedes a owl:TransitiveProperty .
:poisoning a schema:Event ; prov:endedAtTime "1815-06-14T20:00:00"^^xsd:dateTime ; :precedes :death .
:death a schema:Event ; prov:startedAtTime "1815-06-15T06:00:00"^^xsd:dateTime .PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
SELECT DISTINCT ?e1 ?e2 WHERE { ?e1 :precedes ?e2 } ORDER BY ?e1Reasoning mechanism: The TBox declares :precedes a owl:TransitiveProperty; Virtuoso's OWL2-RL entailment then derives any multi-hop :precedes chain (e.g. A precedes B precedes C entails A precedes C) automatically, giving deterministic chronological ordering entirely independent of triple/document order — without the declaration, only the directly asserted pairs would be visible to a query, not their transitive closure.
Illustrative named graph: urn:g:events:king-poisoning
“Alice has citizenship in France — citizenship, employment, ownership each require new predicates in RDF/OWL”
Objection: Citizenship, employment, ownership, and every other relational noun each demand a bespoke predicate, causing unbounded predicate proliferation.
SGF itself concedes it needs 'an open vocabulary' alongside its closed 15-role grammar for exactly this reason. RDFS already supplies that split: new relational predicates (citizenship, employment, ownership) are declared as rdfs:subPropertyOf a small set of superproperties, so reasoning stays bounded to the superproperty set while the predicate vocabulary itself remains open and infinitely extensible — the identical tradeoff SGF claims as its own advantage.
:hasCitizenship rdfs:subPropertyOf :hasRelationalStatus .
:Alice :hasCitizenship :France .PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
# Requires RDFS subproperty entailment (rdfs:subPropertyOf) enabled on the
# endpoint — no triple asserts :hasRelationalStatus directly; it is entailed
# from :hasCitizenship. Without a reasoner, query :hasCitizenship directly.
SELECT DISTINCT ?s ?o WHERE { ?s :hasRelationalStatus ?o }Reasoning mechanism: Virtuoso RDFS++ entailment on rdfs:subPropertyOf makes every new relational predicate automatically visible under its bounded superproperty, without touching the reasoning grammar.
Illustrative named graph: urn:g:relations:citizenship
“"Beethoven composed the Ninth" cannot declare whether this is INFORM, QUERY, or COMMAND”
Objection: A triple cannot declare whether it is being asserted as INFORM, posed as QUERY, or issued as COMMAND.
A closed 13-term act-type vocabulary (INFORM, QUERY, COMMAND, ...) is a SKOS concept scheme like any other. RDF-star lets it annotate the quoted triple directly; where RDF-star is unavailable, the same annotation attaches to the statement's named graph. Nothing about (subject, predicate, object) prevents adding a speech-act annotation — it is absent from the article's examples, not absent from RDF.
<<:Beethoven :composed :NinthSymphony>> :speechAct :INFORM .
:beethovenCompositionUtterance a rdf:Statement ;
rdf:subject :Beethoven ; rdf:predicate :composed ; rdf:object :NinthSymphony ;
:speechAct :INFORM .PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT DISTINCT ?stmt ?act WHERE {
?stmt a rdf:Statement ; rdf:subject :Beethoven ; rdf:predicate :composed ; rdf:object :NinthSymphony ; :speechAct ?act .
}Reasoning mechanism: Classic rdf:Statement reification plus a SKOS-typed :speechAct concept scheme gives every claim a queryable, closed-vocabulary communicative-intent annotation — RDF-star's <<...>> syntax expresses the same thing more compactly, and already does on Virtuoso Open Source Edition. Verified live (2026-08-13) against this document's own demo dataset using reification, since the commercial edition behind the live URIBurner endpoint this document links to will support these RDF release enhancements in an upcoming release.
Illustrative named graph: urn:g:speechacts:beethoven-claim
“Claims lack envelope with identity, provenance, lexical material, and integrity checks for system-to-system exchange”
Objection: A triple, when exchanged between systems, carries no envelope of identity, provenance, lexical material, and integrity checks.
SGF's four required envelope fields — identity, provenance, lexical material, and integrity — map directly onto existing RDF/PROV-O primitives: the graph IRI is the identity, prov:wasAttributedTo/prov:generatedAtTime is the provenance, the triples inside the graph are the lexical material, and a content-hash literal (or a detached signature over the canonicalized TriG serialization) is the integrity check. This is a packaging convention, already standard in Linked Data publishing, not a missing primitive.
<urn:g:claim42> a prov:Entity ;
prov:wasAttributedTo :sourceX ;
prov:generatedAtTime "2026-08-12T00:00:00Z"^^xsd:dateTime ;
:contentHash "sha256:9f3a...c21"^^xsd:string .PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
PREFIX prov: <http://www.w3.org/ns/prov#>
SELECT DISTINCT ?g ?attributedTo ?hash WHERE { ?g a prov:Entity ; prov:wasAttributedTo ?attributedTo ; :contentHash ?hash }Reasoning mechanism: A signed TriG document (named graph + PROV-O metadata + content hash) is the RDF-native admission envelope; Virtuoso ingests and validates it as a single quad-store transaction, and FuXi rules can further check hash/signature consistency before admitting the graph.
Illustrative named graph: urn:g:claim42
“:Composer versus :MusicalCreator — RDF/OWL uses probabilistic similarity scores rather than verifiable proof”
Objection: Reconciling `:Composer` and `:MusicalCreator` in RDF/OWL relies on probabilistic embedding similarity rather than verifiable, deterministic proof.
Reconciling :Composer and :MusicalCreator has had a deterministic, formally specified mechanism since OWL2 in 2004: owl:equivalentClass, computed by a description-logic reasoner. This is the precise opposite of an embedding-similarity score — it is provable entailment under a fixed model-theoretic semantics, the same standard of determinism SGF's own 'deterministic verification' criterion demands.
:Composer owl:equivalentClass :MusicalCreator .
:Beethoven a :Composer .PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
# Unlike owl:sameAs (see :c4), owl:equivalentClass entailment on Virtuoso is
# NOT reachable via a simple query-level DEFINE pragma — empirically verified
# against the live demo dataset: this ASK returns false both without AND
# with `DEFINE input:same-as "yes"` (that pragma is specific to owl:sameAs).
# Exercising it requires a server-side registered RDFS/OWL ruleset via
# Virtuoso's rdfs_rule_set() stored procedure (admin/SQL operation, not
# achievable from a plain SPARQL HTTP query), so this document does not claim
# a working Virtuoso pragma for this specific entailment.
ASK { :Beethoven a :MusicalCreator }
# The portable, verifiable mechanism for this entailment is the FuXi N3 rule
# below, which requires no server-side configuration:
@prefix : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
{ ?C1 owl:equivalentClass ?C2 . ?x a ?C1 } => { ?x a ?C2 } .
{ ?C1 owl:equivalentClass ?C2 . ?x a ?C2 } => { ?x a ?C1 } .Reasoning mechanism: owl:equivalentClass entailment is deterministic and provable under OWL2's formal semantics — computed by a description-logic reasoner (an external DL reasoner such as HermiT/Pellet via the OWL API, a Virtuoso-registered rdfs_rule_set() ruleset, or a FuXi N3 rule) rather than probabilistic similarity. This is a different Virtuoso mechanism from the input:same-as query pragma used for :c4's owl:sameAs entailment — no simple query-level pragma exists for owl:equivalentClass.
Illustrative named graph: urn:g:alignment:composer-musicalcreator
John F. Sowa (co-creator of Conceptual Graphs, ISO Common Logic) posted a separate, independent objection to triple stores in the same week as this rebuttal — not part of the SGF article, but the same underlying claim (the triple is the wrong grain) from a different, prominent source and a different angle: obsolescence and computational efficiency rather than modeling expressiveness. Each of his claims is quoted and answered below using the same working-RDF evidence (named graphs, PROV-O, OWL2/FuXi entailment, and the live SPARQL demonstrations) as the twelve SGF corrections above. Read the original post.
“The triple store representation used by OWL and related software is obsolete.”
Objection: Every triple store deployment, regardless of scale or workload, is dismissed with a single unqualified adjective — no benchmark, no comparison criteria, and no acknowledgment of RDF's ongoing W3C standardization work are offered as support.
The claim carries no benchmark, no citation, and no definition of what 'obsolete' would even mean for a data model still receiving active W3C standardization work (RDF 1.2, including RDF-star, SPARQL 1.2) and running production-scale deployments (DBpedia, Wikidata, the EU Publications Office, and many other Virtuoso instances — both internally and across the wider LOD Cloud — run production systems comprising billions of triples, handling unpredictable usage loads at high performance and scale, including federated SPARQL queries across independently-operated endpoints). This document's own :demoQ9 is a small, concrete counter-demonstration: a single owl:sameAs triple, on a live Virtuoso endpoint, transitively pulls in 71 independently-published DBpedia types for one entity — federation across organizational boundaries that a from-scratch, non-RDF representation would have to rebuild from zero. Asserting obsolescence without engaging any of this evidence is not itself evidence.
:Beethoven owl:sameAs <http://dbpedia.org/resource/Ludwig_van_Beethoven> .DEFINE input:same-as "yes"
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
SELECT DISTINCT ?type WHERE { :Beethoven a ?type }Reasoning mechanism: Same mechanism as :c4/:demoQ9 — verified live: 71 independently-published DBpedia types entailed through one locally-asserted owl:sameAs triple, evidence of an actively-used, actively-federating data model, not an obsolete one.
Illustrative named graph: urn:g:identity:beethoven
“OWL and triple stores should never be used for new systems.”
Objection: A prohibition covering every new system unconditionally, with no carve-out for systems that specifically need to interoperate with the existing Web of Linked Data — the one workload where RDF's schema-free mergeability across independently-designed graphs is the entire point, not an afterthought.
RDF's value for a NEW system is not nostalgia for legacy data — it is that any two independently-designed RDF graphs can be unioned without schema negotiation, because every term is a globally-dereferenceable IRI rather than a locally-scoped internal identifier. A new system built on a closed internal representation (conceptual graphs included) gains nothing from that property until it re-implements an equivalent open, IRI-addressed identity and vocabulary-reuse layer — at which point it has re-invented, not avoided, what RDF already provides as a W3C-standardized, multi-vendor-implemented baseline. 'Never' would have to mean 'never build a system that needs to interoperate with the existing Web of Linked Data,' which is a much narrower and more defensible claim than the one actually made.
:hasCitizenship rdfs:subPropertyOf :hasRelationalStatus .
:Alice :hasCitizenship :France .PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
SELECT DISTINCT ?s ?o WHERE { ?s :hasRelationalStatus ?o }Reasoning mechanism: Same mechanism as :c9 — a new system can mint new relational predicates freely (open vocabulary) while an RDFS reasoner keeps them bounded under a small, agreed superproperty set (closed grammar) — the interoperability property a closed internal representation has to build separately, not one triples lack.
Illustrative named graph: urn:g:relations:citizenship
“rapid response to complex questions on large amounts of data ... without requiring immense numbers of Nvidia chips”
Objection: Majumdar demonstrated rapid response to complex questions on large amounts of data on a laptop, and Permion customers operate significantly larger datasets without requiring immense numbers of Nvidia chips or massive cooling infrastructure.
Production triple stores (Virtuoso among them) are relational/column-store-derived quad engines that run on ordinary CPUs — the SPARQL queries this document links to, including the 71-row entailment demonstration in :sc1, execute against a public Virtuoso endpoint with no GPU involved at any point. 'Without needing Nvidia chips' is true of RDF triple stores too; the actual axis of comparison Sowa draws (conceptual graphs vs. GPU-heavy LLM/embedding systems) is a real and interesting one, but it is not a comparison between conceptual graphs and triple stores, so it cannot support a conclusion specifically against triple stores. No published benchmark comparing Permion against an equivalently-indexed triple store on equivalent hardware and workload is cited, so the architectural claim remains anecdotal rather than demonstrated.
GRAPH <urn:g:beethoven-1824-composition> {
:beethovenNinthComposition a schema:CreateAction ;
schema:agent :Beethoven ;
schema:result :NinthSymphony ;
schema:location :Vienna ;
schema:startTime "1824"^^xsd:gYear .
}PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
CONSTRUCT { :beethovenNinthComposition ?p ?o }
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/why-the-triple-is-the-wrong-grain-rebuttal-demo-data-claude_code-1.ttl>
WHERE { :beethovenNinthComposition ?p ?o }Reasoning mechanism: Same query as :demoQ1 — verified live on a CPU-based Virtuoso quad store with sub-second response, demonstrating the 'no GPU needed' property Sowa attributes to Permion is not distinctive to non-RDF systems.
Illustrative named graph: urn:g:beethoven-1824-composition
“easily accept triples from legacy software”
Objection: Newer systems can easily accept triples from legacy software.
The framing treats triple-acceptance as a one-way legacy-compatibility feature. What actually makes triples mergeable without prior coordination is that RDF has no closed, negotiated schema to align first — two organizations that never talked to each other can publish RDF about the same real-world entity and have a third party union the results correctly, exactly as :c7's conflicting-claims demonstration and :demoQ9's cross-organization sameAs entailment (this document, this session) both show. That is a forward-looking interoperability property for systems that do not yet exist and have not yet agreed on anything, not a backward-looking accommodation for systems that already do.
GRAPH <urn:g:sourceA> { :Beethoven :birthPlace :Bonn }
GRAPH <urn:g:sourceB> { :Beethoven :birthPlace :Koblenz }PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
SELECT DISTINCT ?g ?place
FROM NAMED <urn:g:sourceA>
FROM NAMED <urn:g:sourceB>
WHERE { GRAPH ?g { :Beethoven :birthPlace ?place } }Reasoning mechanism: Same mechanism as :c7 — two independently-authored, uncoordinated graphs merge cleanly with both claims preserved and attributed, never averaged or silently dropped, which is the schema-free mergeability property new, mutually-unaware systems benefit from just as much as legacy-import scenarios do.
Illustrative named graph: urn:g:sourceA
The twelve corrections above are not only documented as Turtle/SPARQL snippets — they are instantiated as real instance data in a companion flat Turtle file, loadable into a quad store (e.g. Virtuoso) as a single named graph, or split across several named graphs using each correction's :namedGraphExample IRI as a starting point. The eleven SPARQL queries that run against this dataset are in the SPARQL Workbench below, not repeated here.
Holds the corrected instance triples for all twelve corrections, c1 through c12. Reification (rdf:Statement + prov:wasAttributedTo / :epistemicStatus / :contentHash) carries the source-attribution, epistemic-tier, and admission-envelope distinctions for c2, c3, c7, and c11. Load as one named graph, or split across several using each correction's :namedGraphExample as a starting point — the eleven SPARQL queries in the Workbench below run against it live.
Interactive graph visualization derived from the companion RDF. Click nodes to resolve, drag to explore. Graph data embedded from companion RDF at generation time.
Query this knowledge graph on URIBurner. The editor opens on the canonical SAMPLE entity-type summary (DAV named graph). Pick a recipe, edit freely, then run live or copy.
Reproduced verbatim from the companion RDF. Execute loads the query into the workbench below and runs it live.
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
CONSTRUCT { :beethovenNinthComposition ?p ?o }
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/why-the-triple-is-the-wrong-grain-rebuttal-demo-data-claude_code-1.ttl>
WHERE {
:beethovenNinthComposition ?p ?o .
}Verified live against the loaded demo dataset. Returns the whole schema:CreateAction event (agent, result, location, time) in one query — nothing about it was 'shattered'. If you load this event into its own named graph (see :c1 :namedGraphExample), swap the FROM/subject for GRAPH <that-iri> { ?event ?p ?o } for atomic graph-scoped retrieval.
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX prov: <http://www.w3.org/ns/prov#>
SELECT DISTINCT ?claim ?place ?source
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/why-the-triple-is-the-wrong-grain-rebuttal-demo-data-claude_code-1.ttl>
WHERE {
?claim rdf:predicate :birthPlace ; rdf:object ?place ; prov:wasAttributedTo ?source .
}Verified live: returns BOTH :claimBirthPlaceBonn and :claimBirthPlaceKoblenz, each with its own source attribution — never a merged/averaged third place. Loading each claim into its own named graph (per :c7 :namedGraphExample) makes the same point via GRAPH ?g { :Beethoven :birthPlace ?place } instead of reification.
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT DISTINCT ?claim ?subject ?predicate ?object ?tier
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/why-the-triple-is-the-wrong-grain-rebuttal-demo-data-claude_code-1.ttl>
WHERE {
?claim a rdf:Statement ;
rdf:subject ?subject ; rdf:predicate ?predicate ; rdf:object ?object ;
:epistemicStatus ?tier .
}Verified live: separates the documented birthplace fact (:DocumentedFact) from the inferred birth year (:Inferred) by claim-level :epistemicStatus — the tier a FuXi N3 rule would otherwise materialize from named-graph membership onto each triple.
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
SELECT DISTINCT ?person ?relation ?value
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/why-the-triple-is-the-wrong-grain-rebuttal-demo-data-claude_code-1.ttl>
WHERE {
?person ?relation ?value .
FILTER(?relation = :hasCitizenship)
}Verified live: Alice's citizenship is declared via :hasCitizenship, a rdfs:subPropertyOf :hasRelationalStatus — new relational predicates stay open-vocabulary while an RDFS reasoner keeps them bounded under the small superproperty set. Under RDFS entailment, ?relation = :hasRelationalStatus also matches via subproperty inference.
DEFINE input:same-as "yes"
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
ASK { :Beethoven a <http://dbpedia.org/ontology/Person> }Verified live both ways: WITHOUT the DEFINE input:same-as pragma this ASK returns false (:Beethoven carries no direct rdf:type to dbo:Person, only owl:sameAs to the DBpedia resource); WITH it, true — deterministic OWL2 entailment via Virtuoso's built-in same-as reasoning (no named ruleset needed), not a probabilistic similarity score. Deliberately has no FROM/GRAPH restriction: the entailment bridges our locally-asserted owl:sameAs triple with DBpedia's own rdf:type assertion, which lives in a different graph elsewhere in the store. See :demoQ9 for a SELECT variant showing the full scale of what gets entailed (71 types, not just one boolean).
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
PREFIX prov: <http://www.w3.org/ns/prov#>
SELECT DISTINCT ?first ?firstTime ?second ?secondTime
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/why-the-triple-is-the-wrong-grain-rebuttal-demo-data-claude_code-1.ttl>
WHERE {
?first :precedes ?second .
?first prov:endedAtTime ?firstTime .
?second prov:startedAtTime ?secondTime .
}Verified live: explicit :precedes plus xsd:dateTime literals recover chronology (poisoning before death) regardless of narrative order.
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT DISTINCT ?statement ?subject ?predicate ?object ?speechAct
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/why-the-triple-is-the-wrong-grain-rebuttal-demo-data-claude_code-1.ttl>
WHERE {
?statement a rdf:Statement ;
rdf:subject ?subject ; rdf:predicate ?predicate ; rdf:object ?object ;
:speechAct ?speechAct .
}Verified live: the reified statement (the defendant's utterance, and separately Beethoven's composition claim) carries its own :speechAct — belief-about-the-claim and the underlying triple are two distinct, independently queryable resources. RDF-star's <<...>> syntax expresses the same distinction more compactly, and already does on Virtuoso Open Source Edition; the commercial edition behind this document's live URIBurner endpoint will support these RDF release enhancements in an upcoming release, so reification is the verified mechanism used here.
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX prov: <http://www.w3.org/ns/prov#>
SELECT DISTINCT ?claim ?subject ?predicate ?object ?attributedTo ?generatedAt ?hash
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/why-the-triple-is-the-wrong-grain-rebuttal-demo-data-claude_code-1.ttl>
WHERE {
BIND(:claim42 AS ?claim)
?claim a rdf:Statement ;
rdf:subject ?subject ; rdf:predicate ?predicate ; rdf:object ?object ;
prov:wasAttributedTo ?attributedTo .
OPTIONAL { ?claim prov:generatedAtTime ?generatedAt }
OPTIONAL { ?claim :contentHash ?hash }
}Verified live (exactly one row): returns :claim42's identity (its own IRI), lexical material (rdf:subject/predicate/object), provenance (prov:wasAttributedTo/generatedAtTime), and integrity check (:contentHash) in one query — the four SGF 'admission context' fields, already RDF-native. Other reified claims in the dataset (:claimComposedNinth, :claimBirthPlaceBonn/Koblenz) also carry prov:wasAttributedTo but not generatedAtTime/contentHash — drop the BIND to compare all of them side by side. Packaging this claim as its own signed named graph (per :c11 :namedGraphExample) moves identity from the claim IRI to the graph IRI, same four fields.
DEFINE input:same-as "yes"
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
SELECT DISTINCT ?type
WHERE { :Beethoven a ?type }Verified live: without the DEFINE input:same-as pragma this returns 2 rows (our own asserted schema:Person and :Composer); with it, 71 rows — the full type hierarchy (YAGO categories, schema.org, foaf, dbo, ...) that DBpedia's Ludwig_van_Beethoven resource carries, all entailed transitively through the single owl:sameAs link. A far more vivid demonstration of what OWL2 entailment actually pulls in than :demoQ5's bare true/false ASK — same underlying mechanism, richer evidence. See :demoQ11 for the same query with the pragma commented out, for a direct one-click A/B comparison.
# DEFINE input:same-as "yes" -- uncomment this line to enable reasoning and compare results
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
ASK { :Beethoven a <http://dbpedia.org/ontology/Person> }Verified live: identical to :demoQ5 except the DEFINE input:same-as pragma is commented out (a plain SPARQL '#' comment, inert until you delete the leading '# '), so this returns false — no direct rdf:type assertion, only owl:sameAs. Uncomment that one line and re-run to see it flip to true, exactly as :demoQ5 does — the fastest way to see OWL2 entailment turn on and off for yourself.
# DEFINE input:same-as "yes" -- uncomment this line to enable reasoning and compare results
PREFIX : <https://medium.com/@JamesStakelum/why-the-triple-is-the-wrong-grain-1cad99e136f4#>
SELECT DISTINCT ?type
WHERE { :Beethoven a ?type }Verified live: identical to :demoQ9 except the DEFINE input:same-as pragma is commented out, so this returns only 2 rows (our own asserted schema:Person and :Composer) instead of 71. Uncomment that one line and re-run to watch the full DBpedia type hierarchy — YAGO categories, schema.org, foaf, dbo — flood in through the single owl:sameAs link, exactly as :demoQ9 does.
No. The event is modeled as its own resource (e.g. a schema:CreateAction or PROV-O Activity), and the individual triples describe that one resource's properties. Retrieval targets the event IRI or its named graph and reconstitutes the whole event in one query — see :c1.
No. In a quad store (the deployment norm for RDF today, including Virtuoso), every triple is stored as a quad with a graph component. Provenance is structurally mandatory at the storage layer; PROV-O then adds a typed attribution vocabulary on top — see :c2.
Yes, via epistemic-tier named graphs plus a forward-chaining rule (FuXi N3, or an equivalent Virtuoso SPARQL INSERT) that materializes the tier onto every triple in that graph — see :c3.
Via owl:sameAs to a canonical Linked Data identity (DBpedia/Wikidata), closed transitively under OWL2 entailment. This is a deterministic dereference, not the probabilistic similarity the article claims RDF is limited to — see :c4.
Yes — a purpose/function datatype property is ordinary RDF, and a SHACL shape can make it mandatory for every instance of the class, closing the gap the article's under-specified example leaves open — see :c5.
Yes — via classic rdf:Statement reification (verified live against this document's own dataset), or RDF 1.2's more compact RDF-star quoted-triple syntax where the endpoint supports it: either way, the utterance becomes its own addressable node that a separate :speechAct/:assertedBy annotation attaches to, structurally separating utterance from truth-claim — see :c6.
No. Conformant SPARQL 1.1 has no averaging operator; a GRAPH-scoped SELECT returns each source's claim labeled by its named graph, and any reconciliation is an explicit, auditable application-level rule — see :c7.
No — RDF triples form an unordered set and never encoded narrative order to begin with, so there is nothing to collapse. Chronology is asserted explicitly via xsd:dateTime literals and a transitive :precedes property, independent of triple or document order — see :c8.
New predicates are declared, but rdfs:subPropertyOf lets them inherit reasoning behavior from a small, bounded set of superproperties — the same open-vocabulary/closed-grammar split SGF itself relies on — see :c9.
Yes — via a closed, SKOS-typed act-type vocabulary attached to a reified statement (verified live), an RDF-star annotation on the quoted triple where supported, or on the statement's containing named graph. This capability was simply absent from the article's examples, not from RDF itself — see :c10.
Yes — a signed named graph serialized as TriG already supplies all four fields: the graph IRI is identity, PROV-O properties are provenance, the triples are the lexical material, and a content-hash literal (or detached signature) is integrity — see :c11.
No — owl:equivalentClass/equivalentProperty under OWL2's formal semantics gives deterministic, provable entailment, computed by a description-logic reasoner (Virtuoso's built-in engine or an external DL reasoner) — see :c12.
The atomic RDF statement (subject, predicate, object) — the unit the article argues is the wrong grain for meaning transport, and which this rebuttal argues is adequate once paired with named graphs, RDF-star, PROV-O, and reasoning.
An IRI-identified set of triples, forming the fourth (graph) column of an RDF quad; the storage-level mechanism this rebuttal uses to carry provenance, source separation, and epistemic tier.
The RDF 1.2 extension letting a whole triple be quoted and used as the subject or object of another triple, enabling statements about statements — used conceptually here to separate an utterance from a truth-claim about it, though :c6/:c10's live-verified demonstrations use classic rdf:Statement reification instead, since the commercial edition behind this document's live URIBurner endpoint will support these RDF release enhancements in an upcoming release (Virtuoso Open Source Edition already does).
The W3C PROV Ontology for representing provenance: entities, activities, and agents, and the relations between them (wasAttributedTo, wasGeneratedBy, generatedAtTime) — used here as the provenance layer for named-graph corrections.
The Shapes Constraint Language for validating RDF graphs against structural constraints; used here to make a purpose/function property mandatory on a class of instances.
The formally specified, model-theoretic derivation of new facts from asserted OWL2 axioms (e.g. owl:equivalentClass, owl:sameAs, transitive property chains), computed deterministically by a description-logic reasoner.
An open-source Python-based logic reasoning system implementing N3/RIF forward-chaining rules over RDF graphs; used here to materialize epistemic-tier annotations from named-graph membership.
OpenLink Virtuoso's storage model, in which every RDF triple is physically stored as a (subject, predicate, object, graph) quad, with built-in RDFS++/OWL2-QL entailment, SPARQL 1.1, and SHACL validation confirmed working against this document's own dataset. Virtuoso Open Source Edition already supports RDF 1.2 (RDF-star) quoted-triple syntax; the commercial edition behind the live URIBurner instance this document links to will support these RDF release enhancements in an upcoming release, so classic rdf:Statement reification is the confirmed mechanism used throughout this document instead.
The article's proposed replacement claim structure: a VerbHub with 15 fixed semantic-role spokes, a Derivation Tag, and a 7-tier epistemic hierarchy, governed by a closed grammar plus an open concept vocabulary.
A classification of a claim's certainty (fact, inference, guess, etc.); modeled in this rebuttal as named-graph membership materialized into a per-triple annotation via a FuXi N3 rule — see :sgfEpistemicHierarchy for the article's own seven-tier version (CORE_DEFINITION, CONSTITUTIVE, SOURCED, CLAIMED, INFERRED, PROVISIONAL, GHOST).
Give the event its own IRI, type it (schema:CreateAction, prov:Activity, etc.), and attach agent/patient/location/time as properties of that one resource, ideally inside a dedicated named graph. See :c1.
Never assert bare triples in the default graph when provenance matters; use GRAPH <source-IRI> and attach prov:wasAttributedTo to that graph IRI. See :c2.
Separate documented facts, inferences, and guesses into distinct named graphs, then run a FuXi N3 rule (or SPARQL INSERT) that stamps each graph's tier onto its member triples. See :c3.
Add owl:sameAs to DBpedia/Wikidata (or an equivalent authority) and let OWL2 entailment close the identity graph transitively for deterministic disambiguation. See :c4.
Add a purpose or function datatype property to the class and enforce sh:minCount 1 on it via a SHACL shape so no instance can omit it. See :c5.
Reify the triple recording what was said (or use RDF-star where the endpoint supports it), then attach :speechAct/:assertedBy to that statement rather than asserting its content directly. See :c6.
Keep each source's claims in its own named graph and use GRAPH-scoped SELECT to surface all claims with attribution; write any reconciliation as an explicit, reviewable CONSTRUCT rule. See :c7.
Use xsd:dateTime literals and a transitive :precedes/prov:startedAtTime chain to encode chronological order; never rely on the order triples happen to be written or read. See :c8.
When a new relational noun needs a predicate, declare it as a subproperty of an existing, small superproperty set so reasoning stays bounded while vocabulary stays open. See :c9.
Attach a closed, SKOS-typed :speechAct value (INFORM, QUERY, COMMAND, ...) to the reified statement, an RDF-star-quoted triple where supported, or the statement's containing named graph. See :c10.
Wrap the claim's triples in a named graph, attach PROV-O attribution/timestamp properties, and add a content-hash or detached signature over the canonicalized serialization before transmitting. See :c11.
Assert owl:equivalentClass or owl:equivalentProperty between reconciled terms and let a description-logic reasoner (Virtuoso's built-in engine, HermiT, or Pellet) compute deterministic entailment. See :c12.