‹documents

documents

all docs

Documents — real things that know their structure, viewable as any format

Doc of record: [../roadmap/documents-universe/THESIS.md](../roadmap/documents-universe/THESIS.md). This page describes the built document spine; the THESIS

carries the universal law + the phase-by-phase status. Status: the spine is complete — real

owned instances (__dataroot/<id>/{object.data,blocks.json,fields.json}), kinds-as-data

(document/registry.jsonl + document/kinds/<k>/…, 99 kinds incl. the legal-lifecycle family),

symmetric codecs (extract.js/render.js, 6/6 content-faithful round-trip), scanned-PDF vision

OCR, per-instance ownership + lensing + sharing, browser download, file-import, cross-node twin

convergence, and one /document/home. The one deferred boundary is layout-faithful (Acrobat-grade) PDF.

The one idea

A document is a real thing — a doc, a sheet, a presentation, a report, a webpage.

It is genuinely that thing, not a "view of" one. What every document has in common is that it

knows its own structure: a tree of semantic content (heading, paragraph, list, table, image,

code…). Because the structure is known, two things fall out for free:

• you can create one — *"ask a doc to create a doc and it happily creates"*;

• you can view it as anything — a native scene you traverse, content-only markdown the LLM

reads, or a real .docx / .pptx / .xlsx / .pdf file.

The "view it as X" is just a view / projection — the same idea as the view FACE on a thing,

nothing more. It is not a separate concept with its own name; a document is the real thing,

and a format is one of its faces.

A website is the clearest case: an external website is not itself one of our things. We *read*

it into a document's known structure and then see it in our thing-shape — the consistent

structure you already navigate, or clean markdown with the skin stripped off.

The structure — a block list

Every document's structure is a JSON array of blocks, walked by functions/json_list /

functions/json_get exactly as openapi walks its operations manifest. One block per content

unit:

[ {"t":"h1","v":"Title"},

{"t":"p","v":"A paragraph."},

{"t":"li","v":"a bullet"},

{"t":"a","v":"label","h":"https://…"},

{"t":"pre","v":"code text"},

{"t":"img","v":"alt","src":"https://…"},

{"t":"quote","v":"…"},

{"t":"hr"},

{"t":"tr","cells":"a|b|c"} ]

Block types map 1:1 onto the scene vocabulary (h1/h2/h3→heading, p→text, li→bullet,

a→link, pre→code, img→image, quote→quote, hr→divider, tr→table row), so the

scene face is a straight walk of the structure. A presentation reads an h1 as a new slide; a

sheet reads tr rows as cells.

The class hierarchy (real things, one shared base)

thing

└── document a content thing that KNOWS its structure: create it, view it as anything

├── doc a word-processing document → default file: .docx

├── sheet a spreadsheet (tr rows/cells) → default file: .xlsx

├── presentation slides (h1 opens a slide) → default file: .pptx (deck reparented here)

├── report a composed report over data → default file: .pdf

└── webpage a web page read into structure → default face: markdown / scene

Each kind is a real class — it *is* that thing. It overrides only what makes it specific (how

it reads its source, its default file format); the structure, create, and the view faces all

come from document.

The verbs (shared by every kind)

verb

role

create

bring the document into being: author its structure (?brief via oracle, ?text inline, else the shipped sample), SAVE it, present it in the default format

write ($1/?content=)

set the content (create / overwrite) from Markdown, blocks, or text — one call delivers the doc

append ($1/?content=)

add content to the end (update) — grow a doc a piece at a time

get

read the content as clean Markdown, returned (no emit) — feed a model the content, not the markup

clear

empty the content (delete)

source (data)

where a *read* document gets its content — a URL (webpage), a file (doc/sheet); empty for an authored one

read

force a fresh read of source into the structure (refresh)

content

the FACE — the structure as a native scene (thing.render draws it per viewer)

md / text / html

the content-only faces (the LLM lens), via the render organ

docx / pptx / xlsx / pdf

the real file faces — the render organ on need

present (?format=)

view the structure in the asked format (dispatch to the leaf named by it)

run

resolve the structure (author or read) and present it — the one-call invoke

make (?brief=)

author the structure from a brief via oracle, cached by content hash

The two organs (carried node scripts, dependency-free)

• document/extract.js <mode> <file> — the structure READER (deterministic, no LLM). Modes:

html (strip a page's skin), md, text, csv, tsv, ipynb, docx, xlsx, pptx, pdf (born-digital

text layer), images (dump a scanned PDF's page images), roundtrip (the P4 self-test). A

scanned / image-only PDF (no text layer) falls back to a live vision-model OCR (document/_ocr),

content-faithful.

• document/render.js <blocksfile> <format> [outpath] — the structure → a face. md/text/html/csv/tsv to

stdout; .docx / .pptx / .xlsx are OOXML (a hand-built ZIP of XML parts, node zlib, no

deps); .pdf is assembled by hand (content-faithful, not layout-faithful). The DSL conducts; the

organ encodes.

CRUD by chat / the LLM / any other thing

A document is a thing, so its actions are already callable by chat, the model, or any other

action — the value here is not "make it callable" (it always was) but that the model works

intelligently instead of fumbling generic file tools:

• Create / Update — one call with the content the model already produced (Markdown):

functions/call "write" $md (overwrite) or functions/call "append" $md (grow it). No

file handles, no format library, no OOXML — the thing owns its own structure.

• Read — functions/call "get" -> $content returns the document as clean Markdown: the

model is fed the content, never tokens spent on tags, layout, or parsing. (md/text

do the same for a human/HTTP view.)

• Deliver — functions/call "docx" -> $path (or pptx/xlsx/pdf) emits the real file

from that same known structure. Author in Markdown, hand someone a Word doc.

So a chat turn that "writes the report and sends the deck" is two action calls on two things,

each operating on its own content — the structure is the asset; the format is a leaf. Markdown

passed to write/append via functions/call is taken verbatim; over HTTP it is URL-encoded

(?content=), as usual.

Relationship to neighbouring classes (no duplication)

• website (author *our own* site chrome — 14 subclasses) is untouched. webpage is its

mirror: *read someone else's* page into structure. Two clear, non-colliding names — and exactly

the point that "a website is not our thing; we see it in our thing-shape."

• mdsite (render a folder of .md as a scene) stays; doc can read .md and adds export

• authoring.

• deck keeps its design system and slide engine; it is reparented parent =

presentation so the taxonomy is honest (a deck *is a* presentation *is a* document) and it

inherits create + the file faces for free.

• openapi is the *active* twin of this *passive* reading: openapi turns an API spec into

callable functions; a document turns a page/file into a readable structure. APIs as things +

pages as things → the whole internet reached by one uniform tool.

What this buys

• Read the web in your own structure. /webpage/content?url=… is any site as the native

thing-view you already navigate; /webpage/md?url=… is the page without skin — the model reads

meaning, not markup.

• Create once, deliver any format. doc.create authors a document and writes a real .docx;

the same structure is then a .pdf, a scene, or markdown by changing ?format=.

• A new kind is a new subclass. PDF-in, image-in (OCR/caption), .ipynb, .epub — each is

one more parent = document folder with its own reader and (if needed) one file face. The

architecture is settled; growth is additive.

The file class — any local file, natively viewed and edited

document reads a *source* (a URL or an authored brief) into structured blocks. Its sibling

file (parent = thing, /file?p=<path>) is the other half: a native viewer / editor /

manager for any file already on disk. No download — you SEE and WORK the file here, the way each

type renders itself:

• code/text → highlighted code; Markdown → formatted (reuses the chat _rich renderer);

CSV/TSV → a real table; JSON → pretty-printed; image → shown inline (base64 data:

URI); PDF / HTML → embedded in a webview; Word / Excel / PowerPoint (.docx/.xlsx/

.pptx, incl. macro-enabled) → read natively (OOXML unzipped by file/office.js): a doc as

formatted text, a workbook as real tables (one per sheet), a deck as titled slides; a folder →

a browsable list (the manager); anything else → a friendly "no viewer yet" note.

• Text kinds get ✎ Edit → a textarea that saves back to the file (file.save).

• Any viewable file gets 📄 Edit as document (file.import) → the file's bytes are read through

the document codecs into a real, owned document instance you can edit and re-export in any

format (content-faithful round-trip). This folds file onto the one document spine — no parallel world.

• One classifier (file/kind.js, the extension→kind map) + one _file_<kind> leaf per format.

Adding a type = one line in kind.js + one leaf — the same additive growth as a document kind.

convos link any file a transcript referenced to /file, so a coding-agent conversation's file

mentions are clickable and open in their own rendering. Owner-only (reads/writes the owner's machine).

Status / follow-ons

• [x] The full document spine (P1–P6): real owned instances, one store+index, kinds-as-data +

generic fallback, symmetric codecs, per-instance ownership/lensing/sharing, one /document/home.

See the THESIS for the phase-by-phase proof.

• [x] deck reparented under presentation; acme-deck re-skin intact.

• [x] Binary view by inlining; office read via file/office.js.

• [x] Real downloads: functions/servefile + the __content_disposition knob; a document

instance streams a real .pdf/.docx/… to the browser via /<id>/download?fmt=.

• [x] Scanned-PDF OCR: image-only PDFs recovered to text via a live vision model

(document/_ocr, content-faithful).

• [x] File → spine: file.import mints a document instance from any file on disk.

• [x] Legal lifecycle on the spine: legal kinds carry sign/stamp/handoff/revise/history on a

per-instance sha256-pinned ledger (document/{sign,stamp,…,history}, reusing legaldoc's engine);

legaldoc is now doorways onto the spine. See [forms.md](./forms.md).

• [x] Cross-node convergence: a document follows the owner across their fleet via twin-DATA.

• [x] In-place round-trip EDIT (roadmap/office-editing/): open a real .docx/.xlsx/.pptx/.pdf from a

storage place, edit it, and save it BACK to the same place in the same format via the C3 write door

storage.put. Docs and Markdown use one continuous document canvas with one Save; a whole-document

base hash refuses stale saves. Sheets, Slides and agent operations keep independently addressed edits,

so concurrent changes to different cells/blocks survive and conflicting changes surface honestly. The

editing primitives live on document (edit, open, saveback, versions, restore, _edit_write)

so every kind inherits them. Walkable version history uses the sha256-pinned ledger, and a 5,000-row

sheet pages via thing/_list.

Everything is FILE-TO-FILE (the block payload never passes through a DSL local, which caps at 128 KB).

Fidelity is content-faithful, not byte-faithful — stated on screen before save (_fidelity).

• [ ] Layout-faithful (Acrobat-grade) PDF — the one deliberately deferred boundary (byte-surgical

layout; we ship content-faithful). Plus nice-to-haves: .ipynb/.epub kinds, headless extraction

for JS-rendered pages.

Zero Office — the product layer (the suite as a range)

The spine is the engine; Zero Office is the product a person lives in. It is a *range* of named

apps, each a face on a document kind (never a new storage model):

App

Class

Front door

New

Export

Zero Docs

doc

/doc/front

/doc/new

.docx

Zero Sheets

sheet

/sheet/front

/sheet/new

.xlsx

Zero Slides

presentation

/presentation/front

/presentation/new

.pptx

Zero Forms

form

/form/front

/document/kinds

.pdf (typed)

Zero Notes

notes

/notes

/notes/add

—

Zero Drive

mydrive

/mydrive

—

—

Zero Mail

email

/email

—

Gmail/IMAP bridge (untouched)

• One front door for all apps: document/front lives on document at height, so a named app is

just app = 1 + app_start = front + packaging (brand/icon/blurb/noun) + an optional

office_scan (field:value, default class:<self>) / office_new spec. No per-app fork.

• One suite home: /office (class office) — a launcher/storefront that owns no content and

links every app, the kind catalogue, "all my documents", "shared with me", and a bounded projection

of real document files already reachable on disk or in connected Drive.

• One native visual editor, all doors: /<id>/edit emits the shared editable scene primitive—a single

continuous canvas for Docs/Markdown, individual cells for Sheets, and title/body controls inside slide cards. Browser HTML is

generated from the scene; macOS/Windows/GTK/Android/iOS use native text widgets. There is no Office HTML

application and no editor webview. Its state is still blocks.json; blur autosave, add/move/delete, conflict hashes,

versions and save-back all call the existing _edit_write/storage._put path. ?data=meta|blocks

exposes the same bounded structure to an agent; ?ai=1 converts a request to one addressed,

_cap_guard saveact-governed proposal after an owner check performed before inference.

• Existing files are first-class: office/_file_src reuses storage._doc_recent (node, disk, Drive)

and office/_file_row sends every symmetric-codec format—DOCX/XLSX/PPTX/PDF/Markdown/text/HTML/CSV/TSV—through document.open. The native files.pick

capability and the storage disk/Drive detail views converge on that same route. No provider scanner,

file writer, or platform editor is duplicated in Office.

• A format is an Office face, not storage metadata: prose files render in the continuous document

canvas, delimited files in the sheet grid, and each saves back through the same guarded block model. The shared

storage._office_route owns this adoption policy for Office rows, disk details and Drive details.

• Google-native is still Zero-native editing: Google Docs, Sheets and Slides are detected by MIME and

fetched through Drive files.export as DOCX/XLSX/PPTX before entering document.open. The Google UI is

never the editor. Zero persists its canonical blocks and saves an OOXML sibling in the source Drive

folder, leaving the proprietary source untouched.

• No blank first canvas: new Docs/Sheets/Slides get minimal data starters; a legacy truly empty file

draws one first editable block/row/slide control. A web.serve browser must prove its Zero ID before

owner content appears, and the signed-out scene provides that sign-in action directly.

• Infinite kinds, browsable: /document/kinds windows a __dataroot mirror of registry.jsonl

through thing/_list — bounded, ?q=-filterable, category chips, honest count == wc -l. Adding

kind #119 is one registry row + one document/kinds/<kind>/ folder, no code.

• Unlimited, listed, bounded: every "your things" list is thing/_list. document/_office_src

is the render helper; its "all my documents" branch delegates to document/_docidx (count-on-write,

O(1)), with an owner-scoped docscan.sh fallback. document.new stamps typed = 1 for template

kinds so Zero Forms (office_scan = typed:1) windows exactly the typed papers.

• Guarded by roadmap/office-suite/verify-office-suite.mjs (stranded links, catalogue bounded,

packaging).

[native-rendering.md]: ./native-rendering.md

[lenses.md]: ./lenses.md (access posture — a different axis from a document's format faces)