Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

livt URI

A livt URI names one point in the livt repository — a rule, an example, a question, a story, a story map, or a ubiquitous language term. It is the form to reach for whenever a reference has to survive outside the livt repository: a test comment, an issue body, a commit message.

livt://mapping/{story-key}/rule/{rule-id}

The MCP server addresses its resources by these URIs — see Commands for what each one returns — and the generated site anchors its stickies to them. Neither depends on where the site is deployed.

Why not a bare ID

R-02 on its own is not a reference. IDs are numbered within the file that holds them, so every example mapping in this repository defines an R-02, and every rule that has examples defines an EX-01. Even R-13 EX-01 narrows it to nothing — the reader still cannot tell which mapping was meant, and neither can a search. The story key is the part that makes it addressable:

livt://mapping/trace-test-to-rule/rule/R-02/example/EX-01

Relative to a repository

The widening stops there: a livt URI never says which livt repository it belongs to. Its interpretation is uniform — one point in the livt repository at hand — the same shape http://localhost/ has, where every reader agrees on the meaning while the referent depends on where it is resolved. Which repository answers is supplied by the consumer’s own declaration (--root / LIVT_ROOT), so scope lives in the workspace, not in the name. That is what keeps a citation short, and what lets the same reference work in every checkout of the same repository.

The first segment is drawn from a closed set — mapping, story, story-map, ubiquitous — so a future form that names a repository in that position stays open without breaking any URI written today.

URI to page

livt build renders the livt repository as a static site, and this is where each URI lands in it. Paths are relative to the output root.

livt URIPage
livt://mapping/{story-key}mapping/{story-key}.html
livt://mapping/{story-key}/rule/{rule-id}mapping/{story-key}.html#rule-{rule-id}
livt://mapping/{story-key}/rule/{rule-id}/example/{example-id}mapping/{story-key}.html#rule-{rule-id}-example-{example-id}
livt://mapping/{story-key}/question/{question-id}mapping/{story-key}.html#question-{question-id}
livt://story/{story-key}story/{story-key}.html
livt://story-map/{map-name}story-map/{map-name}.html
livt://ubiquitous/{term-key}ubiquitous.html#{term-key}
livt://ubiquitous/{ctx}/{term-key}ubiquitous.html#{ctx}/{term-key}

An example’s anchor repeats its rule for the same reason its URI does: EX-01 recurs under every rule of a board, so #example-EX-01 would land on whichever one happened to be first. The sticky’s own badge still shows the local EX-01 — that is the ID the livt repository numbers — while the link behind it carries the rule.

A term takes a context the same way, and for the same reason: invoice can mean one thing in billing and another in shipping, so the context is part of the address rather than a label on it. The context is optional — a term whose meaning holds everywhere keeps the one-segment form — and the two shapes never resolve to each other.

A retired item has no sticky, so its URI lands on the board with nothing to scroll to. The URI still resolves: ask the tooling, which answers with the item and retired: true. Deriving a page is how a reference is shown, not how it is resolved.

Store the URI, render the URL

Nothing in the livt repository records where the site is deployed. The deployment URL is prefixed onto the paths above only while a page is being rendered, which is why the same livt repository can be served from a local livt serve, from GitHub Pages, and from an internal host at once:

livt://mapping/trace-test-to-rule/rule/R-02
  -> mapping/trace-test-to-rule.html#rule-R-02
  -> https://boykush.github.io/livt/demo/mapping/trace-test-to-rule.html#rule-R-02

A deployed URL is therefore a fine thing to paste into a chat and a poor thing to commit: it goes stale the moment the site moves, and it buries the identity it was meant to carry. Keep the URI; let the URL be derived.

Where each form comes from

  • A URL, to send to a person — take it from the board. Every sticky shows its own ID in the bottom-right corner; clicking it copies the deployed URL of that sticky, so what you paste already points at the exact card.
  • A livt URI, to leave in an artifact — take it from the tooling rather than the board. Every rule, example, and question the MCP server returns carries its own uri, so an agent writing a test can quote the point it is proving without composing the URI by hand.

Keeping the two apart is what stops the site from becoming a second source of identity. The board is for reading and sharing; the URI is what the livt repository actually knows about itself.

Reading one back

A URI left in a test comment is only worth as much as the ability to follow it, and that has to work for a reader who is not running an MCP client — CI, an editor, or someone with just a checkout. livt resolve turns any of the shapes above back into the point it names:

$ livt resolve livt://mapping/trace-test-to-rule/rule/R-02
{ "spec_version": "...", "rule": { "id": "R-02", ... } }

$ livt resolve livt://mapping/trace-test-to-rule/rule/R-02 \
    --format url --base-url https://boykush.github.io/livt
https://boykush.github.io/livt/mapping/trace-test-to-rule.html#rule-R-02

The URL form is derived from the table above rather than restated, so a link the CLI hands out and the anchor the build writes cannot drift apart.