Reading is not querying
search ranks. The planner picks indices, fuses their results and returns an Evidence Bundle with a score on
every match. That is what you want when you know what you are looking for.
inspect blocks lists a module in its own order, one line per block, and consults no retrieval index or
planner. There is no score column because nothing was ranked. It does use provenance’s structural subject index
to recover origins and authorship without making their correctness depend on where a record falls in the ledger.
--contains filters the rows that were read — a substring over the title, the detail, the subject, the tags and
the identity — and it is bounded by the same --limit an unfiltered page is.
The distinction is worth keeping straight, because a filter that looked like retrieval would be a second and much
worse retrieval path sitting next to the one with a cost model behind it. In the interface it is two different
things in two different places: the filter box narrows what is on screen, and s opens the query workspace.
A canonical block carries no name — its identity must not depend on what anyone called the file — so a canonical
row is titled by the origin its registration recorded, read back out of provenance. That is why a canonical
module reads as the files that went into it, and why a brain pulled without its provenance layer shows media
types instead.
An authored semantic relation keeps its label. A protocol relation that deliberately has no label — notably a
catalog placement — is titled Relation · <predicate>, such as Relation · classified_as, rather than
(unnamed). These blocks remain visible because browsing a module is an inventory of its actual composition;
the catalog tree is the higher-level view when you want those placement relations rendered as folders.
The human inspect blocks table and the middle TUI pane both include creator and identity. Provenance rows say
not applicable: they are the evidence used to attribute other blocks, so recursively asking for their own creation
provenance would turn the absence of a second provenance layer into a false warning.
The interface
verified is deliberately narrower than “the bytes are valid”: it means an accepted signature on the snapshot that
introduced the creation provenance vouches for the same actor subject. asserted means provenance names an actor but
that cryptographic link is absent or not accepted. The proof tab remains the independent integrity check.
“The bytes the block names” is not only canonical: a derived block may carry its own datum out of line — a
semantic block whose content names a rendered diagram, an episodic one naming a recording. Its preview shows
both halves, text first, because the statement is what the block claims about the bytes and is the only part a
query can reach; the bytes are drawn beneath it. o and e act on those bytes exactly as they do on a canonical
block’s, and content this brain does not hold — a selective install — is reported under the text rather than
replacing it.
Which brain am I looking at? Several layers select one and only --brain is visible in the command you typed,
so the header names the brain and the layer that chose it (coursework/mechanics by flag, demo/brain by state), and i prints the whole path, the snapshot, which modules are installed and who writes are attributed
to. The project is part of the short form because two projects each holding a metric-a is the ordinary case,
and the path alone does not say which one you got. vitruvio brain state answers the same question outside the
interface.
Reading another project’s brain. p opens a picker: projects on the left, that project’s brains on the right.
Choosing one retargets the whole interface in place — no quitting, no second vitruvio browse with different
flags. The two panes are one decision, in the same order the CLI resolves them: a brain name only means something
inside a project, so moving the project cursor refills the brains beside it.
* is the brain you have open, in both columns — half of what you open this screen to ask is where am I, and the
cursor cannot answer that, because it moves as soon as you start looking around. The list is every project
--project accepts, so vitruvio project register is what puts one in it. escape keeps the brain you had.
This is also what vitruvio browse does when no brain was selected at all: it asks, instead of printing five
flag names at somebody who is trying to look at something. It is the one command that does — every other one
still refuses, because a non-interactive caller cannot answer a question.
Moving around. The cursor starts in the blocks, because that is what you came to read: up and down walk the
evidence. left (or m) goes to the sidebar, landing on the module you are already in; right or enter comes
back into the blocks. In the sidebar, moving the cursor opens that module — there is no second keystroke to
confirm. tab cycles the panes.
The catalog appears below the memory modules as folders: scheme, class hierarchy and canonical source. A broad class
includes sources placed in descendants, while unclassified makes evidence outside every placement visible. On a
canonical row, c opens an interactive class selector. Existing append-only placements are locked; exclusive schemes
permit only one direct placement. The interface dry-runs every change first. For a governed brain it then requires an
active commit-scoped key from ssh-agent, asks which eligible key to use and signs the new snapshot. If signing
fails after the catalog commit, the alert prints the exact vitruvio auth sign ... --snapshot ... recovery command.
Other keys: / filter, s search, p project and brain, c classify a canonical source, t swap between original bytes and their normalized
text view — an HTML block opens on the text view, and t shows its source — ] and [ turn PDF pages, o open in
whatever the desktop uses, e export into the working directory, y copy the block id, n and b page through a
large module, r re-read, ? every binding.
browse needs a terminal and refuses --json. It has no output mode — the three inspect commands above are
the same three reads with an envelope, which is what an agent should drive.
Seeing how a query ran
s opens a query workspace inside browse. The query and optional RFC3339 time window sit at the top;
results stay on the left; the chosen physical plan and its visual evidence stay on the right. A graph expansion
depth is explicit because following one edge and following three are materially different queries. A blank depth
means one hop, zero disables expansion, and the planner caps the request at the project’s graph_expand_max.
The workspace asks for at most 25 matches and does not page them. They are the first ranked matches, not proof that
the brain holds no others; the status says more may exist when the Evidence Bundle reports truncation.
The plan tab names every operator and, per module, the indices the planner actually consulted. “Available” is
not the same as “selected”: an installed vector index may lose to an exhaustive scan on a small module, and the UI
says so instead of drawing a vector view that did not participate.
The other tabs are conditional views over the same execution:
The vector coordinates show relative geometry, not match scores. Vitruvio’s B-tree role is implemented by sorted
parallel arrays because whole-module rebuilds make pointer pages unnecessary; the tab names that engine and draws
its ordered spine rather than inventing stored tree nodes. Hash lookup is still named in the plan when selected,
but has no dedicated diagram: a dictionary probe has no useful internal geometry to visualize. Every diagram is a
bounded inspection view, so a dense neighborhood or wide range can extend beyond what fits in the panel.
Choosing a result returns to the reading view and reveals that exact block.
escape returns without choosing.
What a preview can and cannot show
Previews route on the media type the block carries, never on sniffed bytes. The block says what it is, that claim is part of its identity, and a viewer of a verifiable store must not contradict it.
With
--out the same command writes the bytes somewhere permanent instead, replacing what is there — you named
the destination, so it means it. Every derived destination refuses instead: pressing e in the browser will not
write over a file in your working directory, and cannot write outside it either, because the name it derives comes
from the origin recorded in the block and a pulled brain’s origins are somebody else’s text.
Images and PDF pages need vitruvio[vision] — Pillow and pypdfium2. Without them the preview says which extra
would draw it rather than pretending there is nothing there.
The text caps exist because the pane lays text out on the interface’s own thread, once per paint and again on
every resize, and syntax highlighting runs there too. Past 64 KiB or 2,000 lines the footer reads
showing 64.0 KiB of 3.1 MiB followed by the keys that act on the block, so where the preview ends is never
mistaken for where the file ends. inspect content shows up to 512 KiB of the same text, and --out FILE writes
the whole file.
A preview is a thumbnail, and that is a ceiling rather than a defect. Each character cell carries two pixels,
so a page in a 60-column pane is 60x80 pixels for the whole page — enough to see where the diagram is, not enough
to read the caption. The stored bytes are untouched: a canonical block is named by their hash, so nothing is
recompressed or downscaled at rest, and inspect content --out gives you back a file identical to the one you
registered. What is bounded is the drawing.
To actually read it, in order of usefulness:
t— the normalized text view, if the block has one. Real text, reflowable and searchable. A PDF gets one whenpdf-textcould run — the[vision]extra is installed, or--normalize-with pdf-textwas named; asource register file.pdfwithout either stores the bytes and extracts nothing. An HTML block opens on this view already, since its source is the wrapper and its text is the content; theretgoes the other way, to the tags.o, orinspect content DIGEST --open— hands the bytes to whatever this desktop opens PDFs with, at full resolution. Not a web browser:open,xdg-openorstartfile, so a video goes to a player and a spreadsheet to a spreadsheet program. The temporary file is named after the origin, so the viewer’s title bar says something.- More cells — a smaller terminal font is linearly more resolution.
Content is not evidence
inspect content takes a content address — a block’s blob, or its normalized_view.blob — and not a block
identity. inspect blocks prints both, and the difference is the protocol’s: the block is the knowledge-level
statement that certain bytes were incorporated, and other blocks cite it. The bytes are what that statement is
about.
So exporting content copies it out; it changes nothing and cites nothing. And a digest the store cannot produce
is an error rather than empty bytes, because empty bytes are indistinguishable from an empty file.
What a row tells you when it cannot be read
A block can be a verifiable member of a version and still not be readable: tombstoned by a redaction under an erasure policy, or never installed by a selective pull. Both still appear, marked, in the list and in the interface. That is not politeness about edge cases. A viewer that dropped those rows would make a redacted brain look like a smaller one, and the protocol is explicit that lawful erasure must stay distinguishable from a corrupt store.inspect resolvability counts them per module.
The reasoning behind the interface, including what was rejected, is
ADR-0012.