OTO
concepts · from ARCHITECTURE.md at v0.6.1

Views: a web app the user passes, fed by the graph

the graphnodes, edges, vocabulary
→
app.jsonwhich data files the view asks for, in which shape
→
data filesgenerated by the engine, live or on disk
→
the viewexplorer · reader · yours
A view is a web app you pass; the engine fills its data contract.

A view is a directory of static files that renders data it does not produce, plus app.json (apps/manifest.py): what the app is, its entry file, the data files it wants and in which shape, an optional adapter.js, and requires, the classes, relations and attributes it expects the vocabulary to declare.

Each data file is described in the projection language (apps/projection.py): JSON, evaluated by the engine over the graph payload, so it is inspectable, testable without a browser, and the same live and static.

$nodes

with $where, $sort, $limit, $history and $map; field paths such as $label and $attr.<name>; $out and $in across relations with $select and $derived include, exclude or mark; $evidence; $first, $count, $group; $lexicon, $documents, $findings, $ledger, $edges, $pending; $const, $concat, $format; $include for a projection kept in its own file.

Formats are js-globals (window.X = ...;), json and js-module; an app with no data entry calls the HTTP routes itself and cannot be exported, since a static site needs its data on disk.

An app is found by path or by name: <project>/views/<name>, views/ of any ontology on the machine, or the engine's own oto/ui/. An ontology ships apps under views/; the composition carries them, the self-check validates each against the ontology's own vocabulary (a class or relation a projection names must exist, and requires must hold), oto ontology show lists them, and oto init installs them into the project.

Serving and exporting refuse a project whose vocabulary lacks what the app requires, naming what is missing, rather than render an empty page. Three fixture apps of deliberately different shapes, under tests/fixtures/apps/, with their expected output checked in, keep the contract from becoming any one app's shape.

The engine ships two apps and uses the first when none is named, for oto serve --http and for the site stage alike.

The explorer, under ui/explorer/, is the generic view for every ontology: the graph as a columnar canvas, one column per class in the vocabulary's own order, a legend of class chips that filter, hover to light a neighbourhood, click for a detail panel with the entity's provenance line, attributes, evidence quotes, its connections grouped by relation with an incoming edge named by the inverse the vocabulary declares, a derived edge marked by its rule with its premises, and history.

A dashed node cites no evidence; a dashed edge was derived. Above a threshold of entities it switches to focus mode and draws the neighbourhood of a searched or selected entity, hop by hop.

It is ported from the Ascent prototype's reusable graph explorer and made vocabulary-driven: the columns, the palette and what counts as evidenceable are derived in defaults.js, and an ontology or a project may tune them with a views/explorer.json (columns, colours, icons, evidenceable classes, threshold, title), which the app's manifest names under overrides and the engine serves and exports beside the app when present.

It needs React and React Flow, so its source lives in ui-src/explorer/ with one esbuild step (npm run build in ui-src/) and the built bundle is committed, so a user installs nothing and the app makes no network call beyond the engine. Its pure modules, the payload adapter and the defaults, are tested under Node.

The reader, under ui/reader/, is the page-shaped alternative, served with --view reader. It is plain HTML, CSS and JavaScript with no build toolchain and no network beyond the engine, and its app.json asks for the whole payload as data.json ({"$graph": true}), so a static export needs nothing the site stage does not already write.

Seven pages behind hash routes: home as the map (counts, classes, the most connected entities, recent changes, the corpus frontier, the pending count when the lane is on), a section per class, the entity card with provenance, aliases, attributes typed and derived, relations grouped by name with derived ones marked and their premises expandable, history, findings, sources and evidence quotes, the document page with its passage and everything that cites it, search over entities and passages, findings and rules, and the ledger.

Both themes, phone width, no external font. In live mode it polls /api/changes and reloads on a new build; opened from disk it says "static". Its pure functions (indexing, search, each page's markup) are exported and tested under Node over a real payload, and a Python smoke test serves it and exports it.

A domain's own app is attached the same way either built-in is chosen: --view <name|dir>, with the app under the ontology's or the project's views/, and served live or exported static.

No shipped ontology carries an app yet: a generic view that applies to every ontology is planned, and a domain's own app is written against the contract above and shipped under its ontology's views/.