Appearance
Polarity and Retraction
FreshPolarity is MD-LD's built-in diff authoring system. Every assertion can have a positive (+) or negative (-) polarity, turning the format into an append-only semantic ledger.
The Core Concept
In a traditional document, you edit text — you change what's there. In an MD-LD ledger, you never delete text — you write a new entry that cancels a previous assertion. The history lives in the file; the graph holds only the current truth.
markdown
## Article {=my:article}
[Alice] {my:author} ← add Alice as author
[Alice] {-my:author} ← retract: Alice is no longer the author
[Bob] {my:author} ← add Bob as authorAfter parsing: only my:article my:author "Bob" in quads. The Alice entries cancelled each other.
Polarity Syntax
Prefix any annotation token with - to negate it:
| Form | Positive | Negative |
|---|---|---|
| Literal property | {my:name} | {-my:name} |
| Object property | {?my:knows} | {-?my:knows} |
| Reverse property | {!my:member} | {-!my:member} |
| Type declaration | {.ex:Draft} | {-.ex:Draft} |
Multiple polarities can appear in one annotation:
markdown
## Alice {=my:alice .ex:Employee -.ex:Contractor}This adds ex:Employee and retracts ex:Contractor in a single annotation block.
Two Types of Retraction
Intra-Document Cancellation
When a retraction matches a positive triple in the same document, both cancel out. Neither appears in the final output:
markdown
[Alice] {my:author} ← positive
[Alice] {-my:author} ← negative, matches above → both disappear
[Bob] {my:author} ← positive, survivesResult: quads contains only author=Bob. The remove array is empty.
External Retraction
When a retraction finds no matching positive in the current document, it becomes an external retraction. It is stored in result.remove rather than disappearing:
markdown
[Alice] {-my:author} ← no matching positive in this document
[Bob] {my:author} ← positive, survivesResult:
quadscontainsauthor=Bobremovecontainsauthor=Alice(targets a prior document)
External retractions wait to cancel triples from previously merged documents.
Merge Resolution
The merge() function applies external retractions against earlier documents:
javascript
import { merge } from 'mdld-parse';
const v1 = `# Article {=ex:article .ex:Article}
[Alice] {author}
[Draft] {status}`;
const v2 = `# Article {=ex:article}
[Alice] {-author}
[Bob] {author}
[Draft] {-status}
[Published] {status}`;
const final = merge([v1, v2]);
// quads: author=Bob, status=Published
// remove: [] — all retractions resolvedThe hard invariant: quads ∩ remove = empty set — a triple is never simultaneously present and retracted.
The Ledger Pattern
The real power comes from append-only files where every change is a new entry:
markdown
[my] <tag:you@example.com,2026:>
[j] <my:journal/>
# 2026-05-01 — project started {=j:2026-05-01 .j:Entry j:date ^^xsd:date}
Project Alpha status: [planning] {+my:alpha ?my:status}
# 2026-05-10 — project kicked off {=j:2026-05-10 .j:Entry j:date ^^xsd:date}
{=my:alpha}
[planning] {-my:status}
[active] {my:status}
# 2026-06-01 — project shipped {=j:2026-06-01 .j:Entry j:date ^^xsd:date}
{=my:alpha}
[active] {-my:status}
[done] {my:status}merge([v1, v2, v3]) produces my:alpha my:status "done" only. Every state transition is recorded in the text. The graph holds current truth.
Inline Corrections in Prose
Corrections can appear inline in narrative text:
markdown
[ex] <tag:carol@example.org,2026:>
New student [Alice] {=ex:new-student .prov:Person ex:name} is in our [class] {+ex:my-class !member}.
**Correction:** [Her] {=ex:new-student} name is not [Alice] {-ex:name}, it's [Ellie] {ex:name}.
**Further correction:** I asked her directly — she does not know [Bob] {+ex:bob -?ex:knows}.After generate(parse({text})):
markdown
[ex] <tag:carol@example.org,2026:>
# Ellie {=ex:Ellie .prov:Person label}
[Ellie] {ex:name}The generated document shows only the final resolved state.
Type Migration
Change types by combining a retraction and an assertion:
markdown
## Alice {=my:alice .ex:Employee -.ex:Contractor}If ex:Contractor was asserted earlier in the same document, both cancel. If it came from a prior merged document, it ends up in remove.
Object Link Retractions
markdown
[project-x] {+my:project-x -?my:assignedTo} ← remove the assignment link
[my:team] {+my:team -!my:hasMember} ← remove reverse membershipThese retract relationship triples rather than literal values.
Querying the Polarity State
javascript
const result = parse({ text: myDocument });
// What this document currently asserts
console.log(result.quads);
// What this document retracts from prior state
console.log(result.remove);
// The hard invariant: never overlap
const quadKeys = new Set(result.quads.map(q =>
`${q.subject.value}|${q.predicate.value}|${q.object.value}`
));
const noOverlap = result.remove.every(q => {
const key = `${q.subject.value}|${q.predicate.value}|${q.object.value}`;
return !quadKeys.has(key);
});
console.log(noOverlap); // always trueCRDT Semantics
MD-LD's polarity system implements CRDT (Conflict-free Replicated Data Type) semantics for text documents:
- Each document is an append-only sequence of assertions and retractions
- The
merge()function resolves the combined state deterministically - No coordination is required between authors — merge is commutative for retractions that don't conflict
- The final state is always well-defined
This makes MD-LD suitable for:
- Distributed collaboration without central coordination
- Offline-first applications where edits can be made disconnected and merged later
- Audit trails that preserve the full history of changes
- Version control systems for knowledge bases