Addressed Notes reads
Specification · addressed snapshot reads
This page defines the read contract and its acceptance criteria. It is the design reference; the Notes guide and distributed agent skills describe how to use it. Guides may use different explanations, but must preserve these semantics.
The two read modes
| Request | Meaning | Result |
|---|---|---|
read NOTE | Observe current state | Complete source, content hash, write revision |
read NOTE --at REV | Retrieve retained state | The same snapshot every time, while retained and accessible |
read NOTE --since HASH | Compare prior content with current content | Unified line diff, base hash, result hash and revision |
--toc, --section and --tag select a snapshot view. They never change the
meaning of --since. --linger delivers an initial current snapshot and then
line diffs through its existing SSE transport.
Actions the representation must support
| Assistant action | Required information |
|---|---|
| Orient within an article | Full heading outline with levels and source-derived section IDs |
| Read a section or subsection | Semantic nested section subtree, including child sections |
| Read one paragraph or heading | Exact element ID, source slice, global string and line ranges |
| Generate an edit | Original source spelling, code-point offsets and the original write revision |
| Verify an edit | Read the revision in the acknowledged write receipt |
| Continue during collaboration | Keep the draft baseline separate from newer streamed observations |
Normative snapshot contract
There are two read modes. read returns the current snapshot; --at REVISION
returns the retained snapshot identified by that opaque revision. --since HASH
returns a unified line diff against the current source. A hash is a content
reference, not a write revision. Snapshot views do not change differential reads.
Explicit --format inline-dff remains available for character diffs; patch input
continues to default to inline-dff.
TOC, section and tag selectors return mapped HTML without needing --view html.
They are mutually exclusive and cannot combine with --since. --tag is an exact
generated element ID, not an arbitrary CSS selector. A paragraph ID selects one
paragraph; a section's slug ID selects the entire nested section. --section
accepts a numeric section index or its content-derived ID. --at cannot combine
with --if-match: the former retrieves history, the latter requires the current
revision to match. Missing snapshots or elements return 404. Every read, including
cached historical reads, rechecks current note permissions.
Elements, identifiers and ranges
Snapshot HTML uses real nested section elements and h1–h6 headings:
This abbreviated example omits mapping spans and later section content. Source
ranges count Unicode code points, start at zero, and exclude the end. Line ranges
are one-based and include both endpoints. Ranges refer to original source,
including Markdown syntax and line terminators, never rendered text. Section
ranges include subsections. Heading ranges cover only the heading. Paragraphs
are numbered within their nearest section; lists, quotes, code and tables do not
advance the prose counter. Preamble paragraphs use s0.pN. Skipped heading levels
add a nesting level, without invented empty headings; sibling numbering remains
unique when heading levels change. IDs and ranges are local to one revision.
Section slugs follow the existing editor convention: NFC normalization, lowercase,
remove characters other than Unicode letters/numbers, whitespace, _ and -,
then collapse whitespace to hyphens. Prefix with section-; an empty slug uses
untitled. Duplicate slugs get -1, -2, etc., in document order. No truncation
is applied. Reverse lookup uses the element's exact source range, not reversal of
the lossy slug. Headings use sN.h; paragraphs use sN.pN.
List items and Markdown hints
List items use section-local IDs in document order: s1.li1, s1.li2,
and so on. Nested items get their own IDs; a parent item's range includes its
nested list. Ordered and task-list items use the same rule. Markers, indentation
and line endings belong to the original source range. Lists do not advance the
paragraph counter. Preamble items use s0.liN.
Use read NOTE --tag s1.li2 to select a single item. For Markdown notes,
--view markdown prints the original selected Markdown with generated address
comments before headings, paragraphs and list items. It uses the same validated
HTML snapshot and supports --at, --toc, --section, or --tag:
This is a reading view, not canonical source. Its header identifies the revision
and hash; ranges always refer to original source, excluding generated hints.
Do not write the annotated output back as article content. Default source reads
stay exact and unannotated. --since and --linger retain their existing source
contracts; they cannot combine with the annotated view. HTML-source notes use
--view html instead.
Scoped source and edit commands
A scoped root retains the whole document's data-hash and data-revision, and
adds data-scope, data-source-start, data-source-end and data-source-hash.
Its data-source contains only the selected source slice, not the entire note.
For a mapped element, subtract data-source-start when slicing this local source;
retain absolute offsets in edit commands. A TOC has empty root source, with exact
heading source attached to each heading's data-source. It does not imply the
assistant read omitted section bodies. Full HTML keeps the existing full-source
contract. The CLI checks the returned scope, revision and source hash before
printing. Never write the HTML or a partial source slice as the complete note.
For the fixture paragraph above, a directly generated patch is:
Exact mode refuses concurrent changes with 412. Without --exact, the existing
native RTC merge mode uses the original baseline identities. A later read or
streamed revision must never silently replace the baseline of a prepared draft.
Linger and presence
--linger retains its existing contract: an initial source snapshot, then
SSE-driven, debounced line diffs. It does not stream HTML or scoped fragments.
Read a focused view in a separate command using a revision emitted by linger:
read NOTE --at REVISION --tag s1.1.p1. Pinned reads do not overwrite the live
presence selection with historical offsets. Live scoped reads report their
actual range; a TOC reports no whole-document passage highlight. Selection events
carry their own content hash and must not be interpreted against a different
source. --linger rejects --at, --toc, --tag, sections and HTML before joining.
Server retrieval
On the server, a pinned read performs permission checks and a direct retained baseline lookup, without initializing or reading RTC and without uploading new snapshots. Rendering shares a bounded content cache (16 entries, 8 MiB estimated payload budget); a cold read still loads and parses the complete source once. Current reads retain the existing coherent RTC observation and persistence path. No claim of constant-time cold reads or eliminated live-read storage costs is made.
Acceptance evidence
All tests below run against isolated fixtures. The HTTP harness launches the real CLI and the real Fastify routes on localhost, with fixture storage and RTC authority. It is not production or a live hosted RTC test.
| Requirement | Verified behavior |
|---|---|
| Efficient retrieval | Pinned read bypasses RTC initialization, RTC observation and snapshot uploads. Scope rendering shares a bounded cache. |
| Repeated reads are consistent | Outline and paragraph read at the same revision remain identical despite an intervening edit. Old snapshots remain readable after a later write. |
| Editing is direct | Paragraph offsets and exact source generate a patch accepted by the existing edit endpoint; unrelated source stays byte-for-byte unchanged. |
| Stale writes fail | Exact-mode edit against an intervening change returns 412, without applying the draft. |
| Read scopes are clear | Whole article, TOC, nested section and one paragraph are separately verified; scoped reads do not include unrelated paragraph source. |
| Linger remains compatible | Real CLI receives snapshot B and line diff B→C; --at B --tag … still returns B. SIGINT sends leave. |
| Authorization persists | Revoked note access prevents historical retrieval, even with a valid retained token. |
| Offset integrity | Emoji, CRLF, formatting, skipped heading levels, duplicate titles and repeated projections are tested. |
Measured example
A local fixture containing 100 sections and 400 paragraphs produced these results:
| View | Response size | Local rendering time |
|---|---|---|
| Entire article | 586 KB | 39 ms, cold |
| TOC | 35.6 KB | 1.8 ms, warm median |
| One section | 6.9 KB | 1.8 ms, warm median |
| One paragraph | 2.4 KB | 1.7 ms, warm median |
Source size: 237 KB. Warm measurements use 20 iterations. Timings exclude network, permission checks, and object-storage latency; they are not a service SLA. A cold read still loads and parses the complete source. Current reads retain the existing RTC observation and persistence cost. Historical reads load and validate a retained baseline by direct object key, without listing history.
Reproduce
From a workspace checkout containing dreamlake-server, set CLI_SOURCE to the
companion CLI checkout and ARTIFACTS to an output directory:
The harness emits article, outline, section and paragraph HTML; the generated
patch; the write receipt and exact readback; the linger event stream; and a JSON
report. The benchmark emits benchmark.json. No real notes are modified.