Version 3.12.0 · All versions

All pages

These are the docs as released with Solid Memo 3.12.0. Read the latest docs.

Validation

Solid Memo checks that the data it reads from a Pod, and every change it writes, has the form the app expects. This page says what is checked and when, what happens to data that does not fit, and how it can be repaired.

Solid Memo checks the data it works on against its shapes and the DCAT-AP profile, in the browser:

Text in Markdown is never invalid: CommonMark has no invalid document, so no shape constrains it, and the app shows any text safely (markdown.md). A library release is held to more when it is published, by npm run library:check's Markdown rules, which are about how its text shows, not whether it conforms.

Flow

Validating an instance
The validation screen lists the instance's documents and checks each against its published shape with the SHACL engine.
The diagram's source
flowchart LR
    accTitle: Validating an instance
    accDescr: The validation screen lists the instance's documents and checks each against its published shape with the SHACL engine.
    ui["ValidationContainer<br/>#/validate"] --> uc["validateInstance"]
    uc --> docs["instanceDocumentUrls<br/>meta, preferences, catalog,<br/>every deck's cards + reviews,<br/>every draft's documents"]
    docs --> port["ShapeValidator.validateDocument"]
    port --> pod["pod document<br/>(authenticated fetch)"]
    port --> shapes["ns/shapes/&lt;class&gt;/v&lt;N&gt;.ttl<br/>(published with the site)"]
    port --> engine["rdf-validate-shacl<br/>(lazy chunk)"]

Profiles: DCAT-AP and SKOS

Besides Solid Memo's own shapes, data is held to two published profiles, vendored verbatim under packages/vocab/vendor/README.md (pinned by commit and sha256 in packages/vocab/vendor/manifest.json, which a test checks) and published with the site at /vendor/:

ProfileShapesApplies to
dcat-apDCAT-AP 3.0.1 (SEMIC)catalogs, decks and deck releases, distributions, agents
skosSkoHub skos.shacl.ttl + skos.bestPractice.shacl.ttl, for SKOSthe concept schemes in ns/vocab/

The report

Per document, its URL and status; per subject, "conforms to deck format 2", "deck format 3 is newer than this app knows; skipped", "not a Solid Memo subject", a subject only DCAT-AP has something to say about (a license, say), "written by another app; Solid Memo only warns about it", or a table of severity, property, message and value (DCAT-AP's results marked as such). The summary line says "All 9 documents conform" or "3 violations in 2 documents". A subject is checked in its stored format, so outdated but valid data passes: the format update is a separate matter.

The invalid data policy

validateInstance runs when an instance is opened (Workspace, query ["validation", instance], shared with the developer view) and again after a repair or a format update. What happens when the report does not conform is a preference, sm:invalidDataPolicy, a concept of sm:InvalidDataPolicies (vocab.md):

PolicyWhat the app does
Block the instance (sm:blockInstance)The workspace waits for the check, then shows the Data check notice with the repair in place of every screen. Preferences and the validation view stay reachable, so the policy can be changed.
Set invalid data aside (sm:blockSubject, the default)A deck whose entry, cards or review states do not conform is set aside (setAsideDecks): listed as "Set aside", not offered for study, its pages saying so. The catalog itself or a deck group that does not conform sets the arrangement of the deck list aside (arrangementSetAside): the list shows every deck, but cannot be rearranged, nor its groups renamed or deleted, until it is repaired, as a list a newer version arranged cannot. Any other subject that does not conform (a deck's agents or distribution, the catalog's publisher, the preferences, the instance record) sets nothing aside: the notice reports it, and the write check still refuses a write that would leave it not conforming. Everything else works. The notice names the decks set aside, and says when the list cannot be rearranged.
Warn only (sm:warnOnly)The notice, and nothing else.

Data another app wrote

The pod is shared: another app may list a dataset in the catalog, or add its own subjects to a cards document. A subject is Solid Memo's when it has a Solid Memo class (sm:Deck, sm:Card, …), carries sm:formatVersion, the stamp Solid Memo writes on every subject it writes, is the catalog (catalog.ttl#catalog), or is named by one of Solid Memo's subjects as its dcterms:creator, dcterms:publisher or dcat:distribution: the catalog, a deck's agents and its distribution have no Solid Memo class, and they stay Solid Memo's when another app's rewrite has dropped their stamp (ownership.ts). A review state of another scheduler (any value of sm:scheduler but the sm:sm2 IRI, a literal too) is never Solid Memo's, whatever its class or stamp: its fields are another algorithm's, which need not fit the review-state shape, so it is only warned about (data-model.md). Any other subject is another app's, and shaclShapeValidator.ts marks it foreign: it is still checked, against the shape its class picks (a foaf:Agent against AgentV1) and DCAT-AP, but every result about it, DCAT-AP's included, is a warning. So under every policy it sets no deck aside, leaves the arrangement as it is and blocks no instance, and the document holding it still conforms (and gets its receipt). The developer report shows its results; the notice, which is about violations, does not, and the repair offers nothing for it: a warning is no problem. A subject another app wrote with a Solid Memo class is held to that class's shape, as Solid Memo's own are.

A catalog's or deck group's link to a member another app wrote is that app's data too. When the member is a subject of the same document that is not Solid Memo's (a schema:Dataset, say, which fails DCAT-AP's class check) or a blank node (which fails the shape's sh:nodeKind sh:IRI), the result about the link is a warning, in the check and in the write check alike: the catalog still conforms, the arrangement stays editable, and no repair unlinks the member. The drop-dangling-members repair, and writing the catalog whole, keep such a link; a link to a subject of the document that nothing describes is still the catalog's own problem.

The write check

Every repository write (a deck, its agents and distribution, the catalog, cards, review states, preferences, the instance record) is checked before it is saved: the subjects the write touches against their shapes, and — in a document with DCAT subjects — against DCAT-AP (checkSubjects in shaclShapeValidator.ts, wired into the repositories as checkWrite in appUseCases.ts). A write that would not conform is refused with every problem named, and nothing is saved. Only what the write touches is checked, so a document with an old problem elsewhere can still be written to.

Every caller names where the write goes (WriteContext in writeCheck.ts), and that picks the shapes, as pickShape picks them for a read:

A published release is never written, so no write is checked as one.

A release a learner adds from a link is read, not written, and checked as a document in the "library" context (validateDocument(url, "library")): each subject against its library shape, no DCAT-AP. It is read as anyone reads it, with the fetch the shapes are read with: no login goes to its host. A violation refuses the release; a warning does not.

Repair

planRepair (domain/repair.ts) turns a report into repairs for the problems with one safe answer, written in the subject's own stored format:

ProblemRepair
A deck without a descriptionThe default description, Flashcards: <title>.
A deck without a study direction, or an unknown oneFront to back, as format 1 studied every deck
A review state with half an undo snapshotThe snapshot dropped (as the review-state 1 → 2 migration does)
A malformed due dayRecomputed from the last review and the interval
An agent without a nameNamed after its IRI
A catalog or deck group listing a deck or group the document does not describe (DCAT-AP's class check on dcat:dataset or dcat:catalog)Those links dropped (a member typed sm:Deck or sm:DeckGroup alone is kept, and so is every link to another document)

Every other problem is listed with its document linked, to be fixed there or removed (after a confirmation). Removing a subject also drops the catalog's and the deck groups' membership links to it (dcat:dataset, dcat:catalog), and only those: who else names it, a deck its creator or the catalog its publisher, is left as it is. applyRepairs (solidRepairRepository.ts) reads each document once, applies its repairs, writes it once, and the instance is checked again.

The CI cross-check

CI checks what Solid Memo publishes again with an independent SHACL engine, pySHACL (pinned in scripts/requirements-ci.txt), which also runs SPARQL-based constraints: scripts/shacl_crosscheck.py holds the deck library's index and every version, and the pod catalog documents of the deck format 4, 5 and 6 fixtures (format 5 titles a deck in any language, English or not; format 6 tags its keywords) and of the deck group 1 fixtures (nested groups), to DCAT-AP, and the vocabulary's concept schemes to SkoHub's SKOS shapes, best practice included. It also holds a release published in a pod (the library deck 6 fixtures, which describe their series and publisher themselves) to DCAT-AP with no index beside it, and that release and the draft fixtures (draft deck, chapter and step 1) to Solid Memo's own shapes: the script picks each subject's shape by class, format version and context as pickShape does, and targets it with sh:targetNode. Each version is checked beside the index, but only for the subjects it states something about, targeted the same way: DCAT-AP asks a node only about its own statements and the class of what they point at, so a version cannot break an index node it says nothing about, and the index has a check of its own. The script stops if DCAT-AP starts asking more, or if any document states rdfs:subClassOf, since either would make that untrue. The checks run in parallel, one process per CPU. A disagreement between the engines, or a constraint the browser's engine cannot run, fails CI. It reads ns/ and decks/ from the repository, each file at the IRI the site publishes it under, and needs neither a build nor the network, so it runs as its own job beside the build. Locally: pip install -r scripts/requirements-ci.txt, then npm run crosscheck (which runs python3 scripts/shacl_crosscheck.py, and says what to install if pySHACL is missing).