Show
prikk show reports what one sealed block or one patch changed: each operation, its kind, the
path(s) it affects, and the content it changed (RFC 142).
prikk show <block-id|patch-id> [--format json]
A patch is self-describing — the content is already in it. EditText carries the exact bytes
replaced and their replacement; CreateFile/DeleteNode carry (or point to) the file’s content;
RenamePath/ChangePerm/CreateSymlink carry everything they need inline. Nothing is replayed to
find what changed.
A RenamePath also reports the AUTHOR key id that signed the patch asserting it — the signer is
recoverable in the same answer as the rename itself, never a fact a caller has to look up
separately. This is structural, not a rendering choice: show’s own read type cannot represent a
rename without it, so a patch whose AUTHOR signature is missing fails the command rather than
rendering a rename with no signer.
Three operation kinds — EditText, ChangePerm, ReplaceBinary — are node-addressed rather than
path-addressed, so finding where they apply needs one replay of some lifecycle state — which state
depends on what the target is (below). show pays that cost once per invocation, not once per
operation, and there is no flag to skip it: a surface that named a 32-byte node id where a path
belongs would have failed at the one thing it exists to do. If the state a node-addressed operation
resolves against no longer has that node live — one patch that edits a node and then deletes it
— the edit’s own path reports as unresolved rather than failing the command; every other operation
still renders.
Which forms resolve a node-addressed path:
| Target | Resolves against | Node-addressed paths resolve? |
|---|---|---|
| A patch in a sealed block | that patch’s own state: the block’s starting state (its parent’s, or empty for the first block), folded with the block’s own patches in canonical order up to and including that patch | Yes |
| A queued (committed, unsealed) patch id | the folded baseline — the sealed tip with the queue folded on top, truncated at that patch — exactly the state a seal right now would give it | Yes |
| A bare sealed patch id | nothing — a patch id alone carries no block context, and searching for a block that happens to contain it would make the answer depend on what else is sealed | No — reports unresolved_node_id |
A block’s output is the union of its patches, in the block’s own canonical order.
Truncation matters, for a block’s own patches too, not only a queue. Every patch resolves
against the state at that patch, not its block’s end: if a later patch in the same block renames a
path an earlier one edited, or deletes the node an earlier one edited, the earlier patch’s own
edit-text still names the path (or node) it had at the time — never the later name, and never
unresolved_node_id just because a later patch in the same block went on to delete it. The block
form folds a block’s own patches onto its parent’s state, and the queued form folds the active
queue onto the sealed tip, through two different drivers — but both apply the same per-operation
fold to resolve a path, so the answer is identical before and after seal by construction: sealing
does not change what came before a given patch, only what comes after it.
show renders EditText’s own before/after span — it does not synthesize a unified diff. prikk’s
edits are content-anchored spans, not line ranges, and a line-oriented rendering would assert a
structure the format does not have.
ReplaceBinary and any binary-kind blob report their object id and declared size, never content —
nobody has asked to see binary bytes in a terminal.
A content reference is an id, not a guarantee. CreateFile, DeleteNode’s file preimage, and
both sides of ReplaceBinary name a blob by content-addressed id — and once a text file has been
edited, its pre-edit identity is deliberately never written back to the object store (the content
is reconstructed by replay instead). The exception is a checkpoint — a ref’s first sealed block, and every 64th block
after it along its history — which stores the content of every file live at that block, edited or
not, so content that was live at a checkpoint reports normally. If a referenced blob is simply absent from the object
store, that one piece of content reports as unavailable — a named state carrying the blob id,
distinct from real empty content — rather than failing the command. Every other operation in the
block still renders, and the command still exits 0. This is not evidence of a damaged repository:
the overwhelmingly common cause is a deleted file whose most recent edit made its old identity
unbacked by design, and show cannot tell that specific case apart from any other blob that simply
never made it into the store; a doctor-level surface wanting to would need to check independently
whether the node’s own history includes an edit since that blob was last live.
Damage is different, and fails the command. If the object store reports something more specific
than absence for a referenced blob — a content-hash mismatch, a stored type disagreement, a
malformed payload, or a SNAPSHOT-kind blob named where file content belongs — that is the object
store affirmatively reporting a problem, not an ordinary, by-design gap. show propagates it: the
command fails (exit 1) with a message naming the integrity problem, rather than rendering the
affected content as unavailable and exiting 0. Absence is ambiguous enough to degrade; a reported
error is not.
show never fails because there was nothing notable to report: exit 0 covers every case the
target resolves to, whether or not anything is unresolved or an absent content reference reports as
unavailable. A malformed or missing id is a usage error (exit 2), a well-formed id naming no real
block or patch is a failure (exit 1), and so is a referenced object the store reports as damaged
rather than merely absent — an unavailable content reference never softens any of those, which
stay what they were.
A queued patch can be shown before it is sealed. An id prikk status --format json lists under
the queue — committed, not yet sealed — is looked up in the active WAL when the object store does not
hold it, and renders exactly as it will once sealed — node-addressed paths resolved, from the folded
baseline truncated at that patch (the table above) — plus a queued: yes line in prose and
"queued": true on the patch in JSON (false for everything read from the object store, block or
sealed patch alike). An id found in neither place is a precondition (exit 1) that names both places
and where each kind of id is listed; it is not reported as damage.
Three things are easy to conflate, and show only ever speaks to the first: queued here means
committed to this repository’s own local active WAL — not yet sealed, and never anything received
from elsewhere; sync pending means a patch sync accept has already verified and stored from an
exchange artifact, but not yet sealed into a block; and log means sealed, permanent
history. All three are distinct states a patch passes through, never the same thing twice.
A rename names every signer. A patch may carry several AUTHOR signatures (format 7: another
signer’s copy of the same patch merges in). A rename reports every one: prose lists them all on its
asserted by line, and JSON carries author_key_ids, every signer in canonical order (key id bytes).
author_key_id keeps its meaning — the first signer in that order — so a patch with one signer reports
exactly as before.
--format json carries the same content as the prose form, as show-report-v1. It settles the
format for show and nothing else.
Comparing two arbitrary points in history — not just one block or patch — means reconstructing both
and comparing them, a materially more expensive operation show’s own cost model does not cover.
That is a separate command: prikk diff replays both points (or one point and the worktree)
and shows the difference as unified hunks. show still answers “what did this block or patch change”,
from the patch’s own record.
Claim-to-Source Anchors
| Claim | Source anchors |
|---|---|
| Content comes from the patch payload directly; only node-addressed operations need a replay, once per invocation, against that patch’s own state – its block’s starting state folded with the block’s own patches up to that patch, or (for a queued patch) the folded baseline truncated at that patch. | show.rs, patch_replay.rs |
| An unresolved node-addressed operation is reported, not fatal; every other operation still renders. A bare sealed patch id resolves nothing; a queued patch id resolves against its own truncated folded baseline. | show.rs |
EditText renders its own before/after span; ReplaceBinary and binary blobs report id and size only, never content. | show.rs, output/show.rs |
An absent blob reference degrades that one piece of content to a named “unavailable” state rather than failing the command; every other operation still renders and the command still exits 0. A blob the object store reports as damaged (hash mismatch, type mismatch, malformed payload, or SNAPSHOT-kind) propagates instead — the command fails rather than rendering it unavailable. | show.rs, object_store.rs |
Exit 0 whichever way the target resolves; a malformed or missing id is a usage error, exit 2; a well-formed but nonexistent target id, or a referenced object the store reports as damaged, is a failure, exit 1. | main.rs, args.rs |
RenamePath cannot be rendered without its asserting AUTHOR key id – the read type has no construction that omits it, and a patch with no AUTHOR signature fails the command. | show.rs, author_signing.rs |
Provenance
This guide covers RFC 142’s show command. Comparing two arbitrary points in history, or a point with the
worktree, is RFC 153’s prikk diff, a separate command with a materially different cost.