forms
Forms & typed documents — kinds as data on the one document spine
Historically every form class (nda, invoice, lease, …) referenced a forms.md that never
existed. This is that doc, written after the forms system converged onto the document spine.
Doc of record for the whole subsystem: [../roadmap/documents-universe/THESIS.md](../roadmap/documents-universe/THESIS.md)
(the universal law, the two axes, the phase status). This page is the concrete "how a typed
document works" reference. Sibling: [documents.md](./documents.md) (the base spine + codecs).
The one idea: a form is not a class — it is a KIND, and a kind is DATA
A "form" is a typed document: a document whose kind carries a *schema* (the named fields you
fill) and a *template* (the body those fields drop into). There is no form class and no
per-form class. A kind is one row of DATA plus two small JSON files — never code:
document/registry.jsonl one row per kind: {kind,label,category,format,icon,summary[,legal cols]}
document/kinds/<kind>/schema.json the ordered named fields (label, required, group, hint)
document/kinds/<kind>/template.json the body blocks with {{field}} placeholders
This is the reuse-first mandate made literal: 74 old parent = form classes + 46 old
parent = legaldoc kinds collapsed into ~99 registry rows (deduped by kind). Adding a new kind
is a name + two JSON files — a kind is a name, not a folder (the THESIS law). An unknown kind is
never a dead end: it falls through to a generic envelope, still fully create/edit/export-able,
and can be promoted to a typed kind later.
The lifecycle of a typed document
1. Pick a kind. /document/kinds lists every kind (from the registry); /document/home is the
picker + your instance list. A human taps a kind; an agent calls the same URL.
2. Mint an instance. document.new?kind=<k> creates a real owned instance at
__dataroot/<id>/ — object.data (class/owner/title/kind/ts/docfamily), blocks.json (the
template rendered live), and fields.json (the filled values; this replaced the old per-node
formcache singleton — values live *with* the instance now).
3. Fill it. document.fill (bulk JSON) / document.set (one field) / the field form write into
fields.json; the body re-renders with the values substituted. Unknown keys are schema-filtered.
4. See it / export it. Every face reads the one instance: scene (/<id>), markdown (/<id>/md),
or a real file. /<id>/download?fmt=pdf|docx|xlsx|pptx streams the rendered file to a browser
(correct MIME + Content-Disposition: attachment); the same bytes open device-local on a shell.
5. Own it / share it. The instance carries owner + a grant list. document/_lens_ok is
fail-closed (owner OR grantee); document/share (owner-gated) records a grant without clobbering
keys; document/shared is the "shared-with-me" index. A stranger sees nothing (no leak).
6. Sync it. On create/update the instance emits a signed twin event; it converges to the owner's
other fleet nodes (same ZeroID) via twin-DATA — "my documents" follow the person, deny-by-default
to anyone else.
The legal-lifecycle overlay (legal kinds)
The 46 legal kinds (nda, msa, lease, sha, affidavit, …) carry extra registry columns —
custodian, sign, stamp, filing, parties — and gain a lifecycle overlay on the same
instance, reusing legaldoc's proven engine (its _ledger/_profile/_party_has/_today/… helpers,
not a rewrite). A legal instance gets a per-instance append-only, sha256-pinned ledger.jsonl:
verb
records
gate
/<id>/sign
a consent signature, pinned to the exact content hash
owner or named party
/<id>/stamp
stamp duty (amount/currency)
owner
/<id>/handoff
assign the custodian / licensed professional
owner or party
/<id>/revise
a new content version (new sha256 pin)
owner
/<id>/advance · /refuse
move / reject lifecycle state
owner
/<id>/filing
a regulatory filing record
owner
/<id>/history
the versioned provenance trail (every version, across time)
owner or party (fail-closed to strangers)
The instance page links 🕓 Legal lifecycle → /<id>/history for any kind whose registry row has a
custodian (a human reaches it from where they already are; an agent uses the same URL). The old
legaldoc desk (/legaldoc, /nda, …) is now thin redirect doorways onto the spine — one desk,
not two. This retires the "two journals for one person" duplication the reuse mandate exists to kill.
Why this scales (N + M, not N × M)
Formats are codecs (one per format, reused by every kind); kinds are data (additive). The
engineering cost is one spine + ~12 codecs + M kinds-as-data; the *documents* — the trillions — are
instances (data), each owned, listed, lensed, versioned. See the THESIS for the full argument.
The honest boundary
Codecs are content-faithful (extract → edit → re-emit a clean file; layout regenerated) — this
covers invoices, contracts, resumes, reports, decks, and scanned-PDF OCR (page text recovered via a
live vision model). Layout-faithful, Acrobat-grade PDF (byte-surgical layout preservation) is the
one deliberately deferred boundary. State which bar a face meets; never imply the higher one.