User guide · Phase 1
Drafting in DraftLint
DraftLint is a contract editor that treats your document as structured law — articles, sections, clauses — not loose prose. This guide covers how to use the editor today and the underlying vocabulary you’ll see referenced in issues, PRs, and the schema.
01 Using the editor
Opening a document
The editor lives at /dev/editor. A sample Stock Purchase Agreement loads by default so you can see numbering, outline, and signature rendering without typing anything first.
To start fresh, click New in the topbar. You get a minimal scaffold — one article with one section and one empty body paragraph. The numbering plate already reads Article 1 / Section 1, and the caret lands inside the empty paragraph ready for typing.
To switch between the bundled sample documents, click Examples in the topbar (or open the Menusheet on mobile and pick from the Examples section). Today there are two: the default Stock Purchase Agreement and a longer, more cross-reference-heavy Franchise Agreement. Switching examples replaces the current document — there’s no persistence yet, so any unsaved edits in the current sample are dropped.
Empty headings show muted placeholder copy — “Untitled article”, “Untitled section” — until you type into them. The placeholder is purely visual; it never enters the document text and never persists.
The document list
When you’re signed in, your saved documents live on a dashboard at /dev/editor/list. Each row is a document; click it to open that document in the editor. New document creates a fresh Untitled document and opens it straight away. From inside the editor you can return here any time with the Documents link in the topbar.
The list is separate from the /dev/editor playground. The playground always loads a sample and keeps your work only in the browser — nothing typed there is saved. Anything you reach from the document list, by contrast, is a real saved document tied to your account.
Saved documents
Opening a document from the list takes you to /dev/editor/[id] — the same editor, but bound to that one saved document. Its title shows in the topbar and you can edit it there; the rest of the editor works exactly as it does in the playground.
This is distinct from the unsaved /dev/editor playground: a document opened by id belongs to your account and has a stable address you can return to, while the playground is a scratch space that always starts from a sample.
Saving your work
Saving is explicit. Press ⌘S (Ctrl+S on Windows/Linux) or click the Save indicator in the topbar to write your changes. There is no autosave — nothing is persisted until you save.
The indicator tells you where you stand. It reads Saved when everything is written, flips to a Save button reading Unsaved · Save the moment you make an edit, and shows Saving… while the write is in flight. If the document was changed on another device since you opened it, it reads Changed elsewhere and saving pauses until you reload.
If you try to close the tab with unsaved changes, the browser warns you first. This applies to saved documents only — the in-memory /dev/editor playground is never saved and never warns.
If your document is edited elsewhere
The same document can be open in more than one place — another tab, another device, a colleague’s screen. If someone else saves a change before you save yours, your next save can’t go through: writing it would silently overwrite their work. Instead of losing either version, the workbench stops and tells you.
You’ll see a banner across the top of the editor reading This document was changed elsewhere. Reload to continue. While the banner is up, saving is paused and the editor is locked, so you can’t pile up edits that would be thrown away. You have two choices:
- Reload — fetch the current version from the server and pick up editing from there. Anything you typed since the last successful save is discarded.
- Discard local changes — the same outcome, stated plainly: drop your unsaved edits and load the current version.
There is no automatic merge today — whichever copy was saved first wins, and reloading gives you that copy to continue from. If you had substantial unsaved work, copy it out (for example into a scratch document) before you reload.
Checking out a document to edit
A saved document opens read-only. To make changes, choose Check out from the mode control in the top bar — the indicator flips from ○ Viewing to ● Editing. While checked out, ⌘S saves a minor version; choosing Check in records a labelled major version and releases the document. Switch to viewing releases it without recording a version. View preferences now live under View in the top bar.
Your checkout survives a page reload — refresh or reopen the tab and you stay in ● Editing rather than dropping back to read-only. The reloaded document shows its last savedversion, so press ⌘S before reloading to keep recent edits. A checkout you walk away from frees itself after about 15 minutes of inactivity; until then it stays yours, and checking the document out again from another tab simply takes over your own earlier session.
Version history
Every save and every check-in mints a version. Click History in the topbar (desktop, saved documents only) to open the version-history drawer on the right of the paper. Each row shows the version number in major.minor form and a reason badge — checkin, save, or restore. The Major filter keeps only the N.0 check-in rows; All shows every version. Close the drawer with ✕ or by clicking History again.
Previewing a version.Click any row to render that version’s body read-only in the paper — a notice bar at the top reads Previewing N.M · read only. The live editing session is never touched; clicking Exit preview in the banner restores your live editor exactly as you left it. Clicking a different row while in preview loads that version instead.
Restoring a version. While you hold the checkout (the mode control reads ● Editing), an active previewed row gains a Restore button. Confirming the dialog writes the chosen body as a new minor version with reason restore — history is never rewritten, and the new version is the only one you need to audit. After a restore, the editor loads the recovered body and the save indicator reads Saved immediately — the server etag was adopted along with the body, so the next ⌘S goes through without a conflict. If you had unsaved edits, the confirm dialog warns you that they will be discarded.
Comparing two versions
From the same version-history drawer you can compare any two versions side by side. Turn on the Compare toggle to put the drawer in select mode, then click two rows: the first click is the base, the second is the revised version. Clicking a selected row again clears it; clicking a third version drops your earlier pick (the base), makes your previous pick the new base, and sets the third as the revised version. A sticky footer shows your picks as base → revised with a Compare → button that enables once both are chosen. The Major / All filter scopes which versions you can select; it defaults to Major.
Compare → enters a read-only compare mode that replaces the editor with a structural redline of what changed between the two versions — clauses added, removed, moved, renumbered, and amended. A sub-bar across the top shows the version route, the change counts, and an exit ✕. Compare mode is read-only: you can read the differences but you can’t accept or reject them here. To accept or reject redline changes, edit a checked-out document with the live redline instead (see Tracking changes (Redline) below). Pressing the sub-bar ✕ leaves compare mode and returns you to the editor unchanged.
The structural rail carries a filled viewport boxmarking where you are in the document — drag it to scrub. The sub-bar’s ↑ x/y ↓ control (or Alt+↑ / Alt+↓) steps to the previous or next change — added, removed, moved, or amended — and stops at the first and last.
On a phone, tap Menu and choose Version history to reach the same picker — turn on Compare, tap the two versions, then Compare →.
Tracking changes (Redline)
Check the document out to edit, then turn on the Redline toggle from the View ▾ menu. The editor diffs your working draft against the version as it stood when you checked out and shows the changes inline — added text underlined, deletions struck through and kept visible, moved and renumbered clauses tagged. You keep editing normally; the redline trails your typing by a moment and catches up when you pause.
While the lens is on, the analytical highlights (legal lens, defined-term and usage hints) step aside so the changes read cleanly, but cross-references stay clickable and you can still jump to a definition. Check in to snapshot the draft as a new version.
You review changes at three levels — a single word, a whole clause, or the whole document.
Clause. Every changed clause shows a small ✓ / ✗pair in the left margin, level with its heading, the whole time the lens is on — you don’t have to hover to find it. Click ✓ to accept the clause’s change or ✗ to reject it (accepting an added, removed, moved, or edited clause settles it; rejecting puts it back the way it was at checkout). You can also right-click a change to open a menu with the same choices — and on a touch device, long-press a change to open that menu. A renumbered clause has no control of its own: it follows automatically from whatever structural change caused the renumbering, so it is never counted as a change of its own.
Word. To settle one inserted or deleted word on its own, hover that word on a desktop and a smaller ✓ / ✗ pair appears just above it. A word swapped for another (a deletion and an insertion in the same spot) is one change, not two: accepting or rejecting either half settles the whole swap, so you never have to clear the old word and the new one separately. The menu (right-click, or long-press on a touch device) also offers Accept all in clause / Reject all in clause, which settle every change in that clause at once.
Document. A sticky review bar sits at the top of the page while the lens is on, showing how many changes are still open and offering Accept all / Reject all for the whole document. The bar hides itself once there is nothing left to review. The same two verbs live in the Actions ▾ menu on a desktop and in the Menu sheet on mobile, each shown on the same terms as the bar — while the redline lens is on, you are checked out, and there is at least one open change to settle. Because they touch the entire document, the doc-level verbs ask you to confirm first.
These decisions are permanent at every level: accept advances the checkout baseline and reject discards the edit, and neither is undoable with ⌘Z — which is why Accept all / Reject all go behind a confirmation. The accept/reject affordances appear only while you are checked out with the redline lens on.
The Redline toggle is disabled when you are not checked out — the lens diffs against the checkout baseline, so it only makes sense while editing.
Deleting a document
You can delete a saved document from two places. In the document list, hover (or focus) a row and click the × on the right. In the editor, open the document-actions ⋯ menu in the topbar and choose Delete document. Either way, a confirmation dialog asks you to confirm before anything happens — Cancel or Escape backs out.
Confirming from the list removes the row right away. Confirming from the editor takes you back to /dev/editor/list. The delete is a soft delete: the document drops out of your list but is not erased, so support can recover it until a future trash feature lets you do so yourself.
Signing in
The workbench is gated. /dev/editor and /dev/editor/compare redirect anonymous visitors to /sign-in; once signed in, you land back where you tried to go. The user guide you’re reading right now stays open to everyone so you can decide whether to sign up before doing so.
The far right of the workbench topbar shows Sign inwhen you’re signed out and a user avatar (with a dropdown for account settings + sign out) when you’re signed in. Sign-in and sign-up live at /sign-in and /sign-up. Sign-out drops you back at the marketing landing page.
The workspace
Three regions, top to bottom and left to right:
- Topbar — the masthead. Wordmark on the left, then an Actions dropdown (New document, Find & replace, Scan refs) and the Examples menu. View preferences on the right now live together under a single View ▾ menu — a column-width toggle (Reading ⇄ Full), a Numberingchoice (Default / M&A), a Lens choice, a Redlinetoggle (available only while checked out for editing — see “Tracking changes” below), and a Theme choice (Auto / Light / Dark). For saved documents, a History toggle sits beside the View ▾ menu; pressing it opens the version-history drawer on the right of the paper (desktop only). The Guide button is at the far right. Reading width is the default; Full lets the document fill the column for wide tables or side-by-side review. Theme persists per-browser (defaults to Light).
- Format bar — pinned below the topbar, in three zones. Nav + history on the left (Back / Forward / Undo / Redo); a live breadcrumb in the center showing the section your caret is in (click it to recenter on that section); and the inline-formatting marks (B / I / U) on the right.
- Outline rail— a live table of contents on the left. Click any entry to jump to its heading. Every jump — from the outline, the breadcrumb, a cross-reference, or Back / Forward — centers its target in the viewport and gives it a brief highlight so it’s easy to spot. The active item highlights as you scroll or move the caret. Drag the rail’s right edge to resize it (or focus the handle and press ← / →, hold Shift for larger steps). Double-click the edge — or press Homewhile it’s focused — to reset to the default width. Your chosen width persists per browser.
- Paper — the document surface itself, with a statline below reporting article count, section count, word count, and the active numbering scheme.
On mobile
At viewport widths up to 720 pixels (most phones in portrait), the workbench swaps to a chrome shaped for one-thumb operation. The document stays the product; controls are summoned when needed and tuck away otherwise. Every panel and command from the desktop workspace is still reachable.
Slim topbar and Menu sheet
The topbar shrinks to the DraftLint wordmark on the left and a single Menu trigger on the right. Tapping Menu raises a sheet from the bottom of the viewport holding New, Find, Scan refs, the Guide link, the Default / M&A numbering scheme toggle, the Legal Lens toggle, and the theme toggle. Dismiss by tapping the scrim or the Close link, pressing Esc, or grabbing the handle at the top of the sheet and swiping down.
Bottom rail tray
The four rail panels live in a persistent 32-pixel tab strip pinned to the bottom of the viewport — Outline · Defs · Refs · Legal. Tapping a tab raises a sheet covering roughly the bottom two-thirds of the viewport with the corresponding panel; the same tab strip is mirrored at the top of the sheet so you can switch panels without dismissing. Close the sheet by tapping the active tab again, tapping the scrim or the Close link, pressing Esc, or swiping the handle at the top of the sheet down. Navigation actions inside a panel (jumping to a heading, a definition, a usage, a cross-reference target, or a legal term) close the sheet automatically so you land back on the document.
Selection-anchored format toolbar
The format toolbar is hidden when the caret has no range — fresh cursor, nothing to format. The moment you select text, the B / I / U cluster appears at the top of the document; collapsing the selection back to a caret hides it again. The paper-width and numbering-scheme toggles don’t appear in the mobile toolbar: paper width is locked to Reading on mobile, and the numbering scheme lives in the Menu sheet. If you had Full set on desktop, resizing the viewport back above 720 pixels restores it automatically.
Find dock
The find-and-replace panel docks to the viewport bottom on mobile (above the rail tab strip), where the on-screen keyboard can’t push it out of view. Everything inside the panel — query input, replace input, scope selector, match navigation — behaves exactly the same as on desktop.
Double-tap actions
Double-tapping does different things depending on what’s under the tap:
- A cross-referencejumps to its target — the same destination the inspector’s Jump to targetbutton reaches, without opening the inspector first. The context bar’s Back button returns you.
- A legal term-of-art(a phrase the dictionary recognizes — “consideration”, “force majeure”, etc.) opens the term inspector with the dictionary entry. On desktop the inspector also pops on hover; on mobile, double-tap is the explicit gesture (a single tap just places the caret, so the inspector doesn’t crowd you on every accidental touch).
- A defined-term usage views its definition without moving the caret — if the term is also a legal term-of-art it pops the term inspector, otherwise it scrolls to and briefly highlights the in-document definition. (This is the View definition item from its long-press menu.)
- A definition views its first usage — scrolls to and briefly highlights the first occurrence. (The View first usage item from its long-press menu.)
- A phantom-detected span— a capitalized phrase that looks like it might be a defined term but isn’t — offers a single Define this term action that opens the define-term popover pre-filled.
- Any other word in the prose opens the define-term popover with that word pre-filled.
Double-tap is the default action; long-press opens the full action menu for whatever’s under it (see below).
Long-press to open the action menu
Hold your finger on a marked span for about half a second to open a small action menu anchored to it. The menu’s items vary by what you long-pressed:
- A definition: View first usage, Jump to first usage, Jump to next usage, Jump to last usage, Rename, Remove definition.
- A usage: View definition, Jump to definition, Jump to next usage, Jump to last usage, Rename.
- A cross-reference: View target, Jump to target, Re-target… (reopens the cross-reference picker over the existing link so you can point it elsewhere or change its format), and Remove cross-reference.
- A broken cross-reference (its target no longer exists): Why is this broken? (opens the inspector explaining the breakage), Re-target…, and Remove cross-reference.
- A phantom-detected span: Define this term.
View … items bring the target into view without moving the caret — for terms they scroll to and briefly highlight the span; for a cross-reference they scroll to the destination node. One exception: when the usage you long-pressed is also a legal term of art (one of the dictionary-matched spans), View definitionpops the Term Inspector with that term’s dictionary definition in place, rather than scrolling to the in-document definition — Jump to definition still navigates there. Useful when you want to glance at something without losing your typing position. Jump … items move the caret to the target. Rename, Remove …, and Re-target… close the menu on confirm.
Plain prose isn’t affected: long-pressing body text still brings up the system text-selection menu as usual. Dismiss the action menu with the × button, a sideways swipe on the handle, a tap outside, or Esc.
First-visit hint
On the first mobile visit per browser session, a small card floats above the document listing the four core gestures (double-tap, long-press, swipe, context bar). Dismiss it with the × or the Got it button — the dismissal sticks for the rest of the session, but the card returns on the next one so a returning user gets a quiet refresher.
Section breadcrumb
On a long doc, the outline isn’t visible without raising the rail sheet — so a thin breadcrumb pins itself to the top of the viewport as you scroll, showing your current article, section, and heading. Tap it to open the rail sheet directly to the Outline tab. It hides when you make a selection (the format toolbar takes the same slot) and at the very top of the document, before you’ve entered any hierarchy node.
Context bar
Sitting just above the rail tab strip, a 40px bar holds Back, Forward, and Undo. Back and Forward step through the navigation stack — every jump from the outline, a definition, a usage, a cross-reference, or a legal term records the position you were leaving, so Back returns you to it. On desktop, ⌘← and ⌘→ step back and forward through that same navigation stack (they fall through to the normal line-start / line-end motion when there’s nowhere left to go). Undo reverses the most recent edit (the same action as ⌘Z on desktop). The bar tucks away when a sheet, the find dock, or the format toolbar takes over, and auto-hides while you scroll down the document — flick up or return to the top of the page to bring it back.
Reshaping structure
The depth of a heading is a property of the document, not a font size. Use Tab to push the current node one level deeper into the previous peer, and ShiftTab to lift it out. Numbering renumbers immediately. If a move would violate the schema (for example, trying to nest below subclause), the keystroke is silently absorbed — nothing happens to the document.
To reorder siblings at the same level, place the caret inside the node and press Alt↑ or Alt↓. This moves the entire subtree, headings and children together.
Adding new sections inline
Enter behaves differently depending on where the caret sits in a heading:
- At the endof a heading, the caret moves forward into the body — the first paragraph of the current section (or the first nested child’s heading at higher levels). No new section is created; this is the “finish the title, start writing” path.
- In the middle, the heading splits — the remainder text moves into a new sibling at the same level and the caret follows.
- At the start, an empty sibling is inserted before; the caret stays put.
To create a brand-new sibling at the same level after you’ve already dropped into the body, press ⌘Enter. That works from anywhere inside an article / section / subsection / clause / subclause and lands the caret in the new heading. Numbering reflows on its own.
Deleting a section
Two paths, same effect: ⌘⌫while the caret is inside the section’s heading, or hover the row in the Outline rail and click the × button that appears on the right. Children go with the parent — deleting a section takes its subsections, clauses, and subclauses along.
A toast at the bottom of the page confirms the delete and offers Undo (or just ⌘Z). Cross-references that pointed at the deleted node turn into broken refs — the red squiggly underline + Refs-rail Broken section surface them. No confirmation dialog; the toast + undo carry the safety.
The schema requires at least one section per article (and the doc requires at least one article). When deleting would leave a required-non-empty parent empty, the command is a silent no-op. One known limitation: once you delete and save, the node’s stable id is gone for good — recreating the section by hand won’t bring back cross-references that used to point at it. Undo restores it; save-then-undo doesn’t.
Inline formatting
Bold, italic, and underline are the supported inline marks for prose formatting. Toggle them with the toolbar or with ⌘B / ⌘I / ⌘U. The defined-term mark is documented separately below.
The toolbar stays pinned to the top of the viewport as you scroll, and on desktop also carries history + navigation buttons — ‹ Back, Forward ›, Undo, and Redo. Back and Forward step through the navigation stack (⌘← / ⌘→); Undo and Redo are the editing stack (⌘Z / ⌘ShiftZ). Each greys out when there’s nothing to go to, and hovering any of them shows its shortcut. (On mobile these live in the context bar instead.)
Defining terms
Select the text you want to define — typically the quoted, capitalized phrase right after “means” — and press ⌘ShiftD. A small popover appears in the right edge of the document with the selected text pre-filled as the canonical term. Edit it if the canonical form differs from what you selected (for example, selection “Affiliates” with canonical term Affiliate), then press Enter or click Define to wrap the selection. Esc or Cancel dismisses without changes. Definitions render in a brilliant midnight-blue ink, heavy weight, small-caps; in-prose usages of the same term echo the chord at a lighter weight in soft midnight ink — no underline, so the term-network reads at a skim without fragmenting the surrounding prose.
Automatic usage binding
The editor watches for capitalized phrases that match a registered canonical term and acts on its own when the choice is unambiguous:
- Exactly one definition — the match is wrapped in a
definedTermmark silently. The Defs rail’s usage count ticks up; nothing pops over the prose. The auto-bind is its own undo step, so ⌘Z reverts just the bind without erasing the word you typed. - Zero or two-plus definitions — the match gets a faint dotted underline in muted ink (distinct from the numbering-blue phantom underline). Hover or Tab onto the underline to surface a small Bind as usage of “X” pill; Enter or click confirms the binding.Esc dismisses the pill; the underline stays for the next interaction. Auto-binding is held back here because silent binding could introduce the wrong cross-reference.
Behavior is not retroactive: adding a second definition of an already-bound term leaves existing usages alone. Remove the conflicting definition and subsequent matches resume auto-binding.
Jumping from a usage to its definition
⌘click on any defined-term usage moves the caret to the first definition mark for the same term. Same gesture as ⌘click on an xref. Use Ctrlclick on Windows/Linux. Plain click stays a normal selection; only the modifier triggers the jump.
Marking usages manually
Once a term is defined, you can mark later references to it as usages so the Defs rail counts them and rename-cascade picks them up. Select the reference and press ⌘ShiftK. A picker appears listing every canonical term in the document, alphabetically. Type to filter, ↑ / ↓ to move the highlight, Enter to commit, Escto cancel. If the selection text matches a canonical term exactly (case-sensitive), that row is pre-selected — so the common case is “select the word, hit ⌘ShiftK, hit Enter.” The picker is keyboard-first; there’s no toolbar entry yet (the command palette will host one in a later phase). With no definitions in the document the picker shows an empty state pointing back at ⌘ShiftD.
Active term highlight
When the caret sits inside a definition or usage, the active span picks up a saturated green highlight and every sibling — the canonical definition and all other usages of the same term — picks up a lighter shade of the same hue. Move the caret away and every highlight disappears; defined terms read as normal prose at rest. The effect makes it possible to audit a term’s reach from the prose itself, without leaving for the Defs rail.
With the caret on any span, ⌘Shift→ jumps to the next occurrence of the same canonical term and ⌘Shift←jumps to the previous; both wrap at the ends. The definition is part of the cycle at its document position. The same gesture works on terms-of-art spans — see the § 02 keymap. When the caret is not on a qualifying span the default selection-extending motion runs as before. If a span happens to be both an authored defined-term and a dictionary term-of-art, the defined-term cycle wins.
Cross-references
Press ⌘ShiftR to open the cross-reference picker. The picker lists every article and section (and clause / subclause as the doc deepens) in document order, each row showing the computed label — e.g. Section 1.02 — and the first ~60 characters of the heading text. Arrow keys move the highlight; Enter commits; Esc cancels. Typing in the input filters by either the label or the heading text (substring, case-insensitive).
Three formats: Long (Section 4.2(b)), Short (§4.2(b)), and Number-only (4.2(b)). The default is Long; your selection is remembered for the rest of the browser session.
With an empty caret, confirming the picker inserts the computed text wrapped in an xrefmark. With a non-empty selection, confirming wraps the existing text as an xref without changing it — the path for converting prose like “the foregoing Section” into a tracked reference.
Drag to cite
Each row in the Outline rail has a small grip (⠿) — on desktop it appears on hover, just left of the delete ×. Drag it into the document and drop it where you want the citation: a thin caret line tracks the drop point as you move (snapping to word boundaries so the reference never lands mid-word), and releasing inserts an xref to that node — the same result as running the picker at the caret, in one gesture. If the spot you want is off-screen, drag toward the top or bottom edge and the document scrolls to follow — faster the closer you get to the edge. The drop uses your last-used format; a toast offers Change format, which re-opens the picker over the just-inserted reference. The grip is a pointer affordance — ⌘ShiftR remains the keyboard and screen-reader path.
On mobile the grip is hidden — the ⠿ glyph is too small to be a comfortable touch target, and a press-and-hold drag reads poorly on touch. Insert a cross-reference from the picker (⌘ShiftR) instead. Outline rows still tap to navigate.
Tab-to-bind suggestion
As you type a citation that resolves unambiguously to a single hierarchy node — Section 4.2, §4.2(b), Article III, abbreviations like Sec.— the matched text picks up a dashed blue underline and a small chip appears just after it previewing the target’s heading and a Tab hint. Press Tab (or click the chip) to commit the binding in a single undo step — separate from the typing transactions beneath it, so undo reverts just the bind and leaves your prose alone. Esc, moving the caret out of the matched range, or typing past the citation dismisses the suggestion silently. Only confident, unambiguous matches surface — when the resolver isn’t sure, the chip stays out of your way and ⌘ShiftR is the explicit path.
In the body, xrefs render as a thin blue underline (same hue as the numbering plates — they encode the same kind of structural identity). When the caret sits inside one, the underline strengthens. ⌘clickon an xref jumps the caret to its target’s heading (use Ctrlclick on Windows/Linux); plain click stays a normal selection, matching the defined-term gesture. If the target was renamed or removed, the underline turns into a bright-red squiggle (same shape spellcheck uses), and ⌘click flashes the source span instead of jumping anywhere — a visible hint that the reference is broken and needs the picker re-run.
When you add, remove, or reorder a section — or flip the numbering scheme — every xref’s visible text updates in the same transaction. There’s no separate refresh step and no flicker; the renumber and the relabel land together, and a single undo reverts both. Xrefs whose target has gone missing read as a loud [broken ref]placeholder paired with the red squiggly underline; the rail’s Broken section keeps tabs on each one by id so you can find and re-point it. Same-transaction salvage still runs first: if your structural change happens to mint a section whose computed label uniquely matches the xref’s current text, the xref silently rebinds to the new node before the placeholder kicks in. Ambiguous matches stay broken on purpose. Undo always reverts the placeholder swap together with the structural change that triggered it.
Hover the pointer over an xref and a small card surfaces with the target’s full label, its heading text, and the first sentence of its body — along with a Jump to target button that works the same as ⌘clickon the underline. Moving off the xref dismisses the card. Alt-click on an xref shows the card immediately and pins it (no dwell delay, no hide-on-leave). Broken references show a muted “Target not found” card with a pointer back to ⌘ShiftR for retargeting. Press Esc to dismiss a pinned card; typing also dismisses.
Scan refs
The topbar’s Scan refs button finds plaintext citations anywhere in the document — Section 4.2, §§ 1, 2, and 3, Sections 4.2 through 4.5, Article III, parenthesized sub-locators, and the common abbreviations — and routes each match through a review dialog. Each row shows the surface text, the resolver’s best-guess target, and a confidence chip (confident, fuzzy, or no match). Confident + fuzzy rows are checked by default; no-match rows must be overridden via the row’s ⋯ menu before they can apply. Confirming binds every accepted row in a single transaction — one undo step regardless of how many references the scan caught. The scan only surfaces unbound plaintext; spans already wearing an xref mark are skipped.
The Defs rail
The left rail has three tabs: Outline, Defs, and Legal. Switching tabs preserves the outline state — collapsed sections, focus, and scroll position survive the round trip.
The Defs tab lists every defined term alphabetically. Each row shows:
- Term — the canonical string carried by the mark.
- Usage count — number of
definedTermmarks pointing at this term. (Auto-suggested usages land in a later story; today this counts only usages that already carry the mark.) - Snippet — the marked text from the first definition, italicized and muted.
- State stripe — a 2px right-edge stripe encoding state: muted for dead (defined but never used), numbering blue for phantom (placeholder for the upcoming detection pass), warning red for conflict (same term defined more than once).
Clicking a row jumps the caret to the first definition. The kebab (⋯) opens a per-term menu with Jump to next usage and Rename term….
A filter strip at the top of the rail — All / Defined / Phantom— narrows the list. Each chip carries its own count. Defined hides phantoms (so you can audit just the terms with bindings); Phantom hides everything else (so you can triage the “did you forget to define this?” flags in isolation).
Healthy defined-term rows wear a leading 3px midnight-blue marker and render the term itself in the same midnight ink at a heavier weight — the rail reads as every term is anchored, not as “one row out of a quiet list.” The row the caret currently sits inside picks up a saturated wash + an outer glow ring. Dead and phantom rows opt out of the accent so the chord is reserved for terms that actually carry a binding.
Renaming a term
Rename term… opens a preview dialog: the header holds a new-term input, and the body lists every marked surface form of the old term (one row per unique form, most-common first). Each row carries an editable suggested replacement and a Skip toggle. Enter from the header commits; Esccancels with no changes. Apply runs every selected row’s text rewrite and re-attributes every mark — including skipped spans, whose visible text stays but whose term attribute still updates — in a single transaction (one undo step).
Smart-suggest fills in confident transformations only:
- Case —
affiliate→subsidiary,Affiliate→Subsidiary,AFFILIATE→SUBSIDIARY. - Possessive —
Affiliate’s→Subsidiary’s. - Plural —
Affiliates→Subsidiaries. Powered by thepluralizepackage so irregulars likeIndicesandCounselcome out right.
Anything else — verb / adjective derivations (Affiliated, Affiliation), quoted forms (“Affiliate”), multi-word phrases — is left blank-and-not-confident on purpose. The row defaults to Skip; type your own replacement and untick Skip to apply. Better to type than to accept a wrong suggestion in haste.
If the new term you type is already defined elsewhere in the document, Apply is blocked with an inline error so you can’t accidentally create a conflict. Phantoms (capitalized matches with no definition mark) are not touched by rename — only marked spans for the renamed term are.
When every span happens to be an exact-case match of the old term (the common case after defining a clean canonical form), the command-palette / programmatic rename path skips the dialog and applies silently. The rail kebab always opens the dialog so you can see what changed.
Footer totals follow the rhythm N TERMS · N DEAD · N PHANTOM · N CONFLICT in mono uppercase — handy as a glanceable health indicator while drafting.
Phantom detection
A phantom is a phrase that looks like a defined term but has no matching definition. The rail flags three patterns:
- Quoted capitalized phrases —
“Affiliate”used without a definition. The strongest signal; legal drafting almost always quotes a term where it is defined. - Multi-word capitalized sequences —
Material Adverse Effectmid-sentence with no definition. - Single capitalized words mid-sentence —
each BorrowerwhereBorrowerisn’t defined. Sentence-initial capitalization, all-caps acronyms (LLC), and a small stoplist of unambiguous proper nouns (US states, months, days, common country names) are suppressed.
Phantoms surface as rows with a numbering-blue right-edge stripe and the snippet unresolved usage. Clicking a phantom row jumps to the first occurrence so you can wrap it as a definition with ⌘ShiftD or accept it as expected prose. The heuristic intentionally favors false negatives over false positives — when in doubt, it stays quiet.
Not yet shipped:per-document dismissal of phantoms (so “Closing” doesn’t keep nagging you on a doc that uses it casually) and the section-gutter stripe coordination with the gutter feature in Epic 1.6.
The Legal rail
The Legal tab inventories every dictionary term the editor recognizes in the current doc, grouped by rigidity tier in the order Hard → Semi-rigid → Negotiated → Convention. Empty tiers are omitted, so a contract that never touches a Hard term simply skips that section.
Each section header carries a tier dot in the tier’s hue and a count summary in the rhythm N TERMS · N USES. Each row shows the same dot, the canonical term, and a tabular usage count. Clicking a row jumps the caret to the term’s first occurrence; because the rigidity plugin already promotes the caret-adjacent match to .dl-rigidity--current, the jump doubles as a flash without a separate animation.
Where the Defs tab lists terms you have defined in the document via ⌘ShiftD, the Legal tab lists terms drawn from the built-in M&A dictionary — the same entries that drive the per-tier underlines and the Term Inspector. Empty state links to the dictionary section of this guide.
Pasting from Word and Google Docs
When you paste HTML from Word or Google Docs, the editor strips inline styles, spans, and image tags, then promotes paragraphs styled as headings (Word’s MsoHeading, Docs’ title) up to real heading nodes. Tables, images, and footnotes are dropped in Phase 1. Plain-text paste splits on blank lines into paragraphs.
Find and replace
Press ⌘F or click Find in the topbar to open the find panel above the document. Type a query and every hit is highlighted; the active match is emphasized and scrolled into view. ⌘G cycles to the next match, Shift⌘G to the previous one. Esc closes the panel, clears the highlights, and returns focus to the editor.
Three scopes are available, selected from the segmented control in the panel:
- Whole document — the default. Matches everywhere in the doc.
- Current section — narrows to the nearest ancestor
sectionof the caret. Moving the caret to a different section updates the scope automatically. - Selection — locks to the selection range that was active when you chose this scope. Useful for replacing inside a specific clause without touching the rest of the document.
Two option toggles sit beside the scopes: Aa for case-sensitive matching and ab| for whole-word matching. Both can be combined with any scope.
The lower row holds a replacement input plus Replace and Replace all. Replace swaps the active match and advances; Replace all rewrites every match in the current scope in a single, undoable transaction. Replacements always stay inside a single block, so node boundaries (headings, paragraphs, list items) are never crossed.
Accessibility & assistive tech
The editor surface, outline, and find panel are designed for keyboard-only and screen-reader use:
- Each heading in the document carries an
aria-levelmatching its schema depth — article = 1, section = 2, subsection = 3, clause = 4, subclause = 5 — so a screen reader announces “heading level 2” for a section, regardless of how the heading is rendered visually. - The outline rail is exposed as a real ARIA tree (
role="tree",role="treeitem",aria-level,aria-expanded). Only one item is in the tab order at a time; arrow keys move focus inside the tree. - Focus rings are always visible. Closing the find panel with Escreturns focus to the document so you don’t land back at the top of the page.
- No focus traps: Tab moves through the topbar, toolbar, outline, document, and signature block in source order.
Screen-reader walkthrough
The following flow has been verified manually with NVDA 2024 on Windows / Firefox and VoiceOver on macOS / Safari:
- Land on
/dev/editor. The page title is read first, then the topbar landmark, then the document outline tree. - Tabinto the outline. The first article is announced as “Agreement, treeitem, level 1, expanded.” ↓walks down the visible items; ←on an expanded item collapses it (“collapsed” is announced).
- Press Enteron any treeitem to jump the caret into that heading in the editor. Type-ahead works too — pressing “s-i-g” in quick succession jumps to the next heading whose text starts with “sig”.
- In the editor, headings are announced with their schema-derived level. Pressing ⌘F opens the find panel; the find input is auto-focused. Esc closes it and returns focus to the document — the screen reader picks up reading from the caret.
Signature block
The footer of the sample document renders a real signature block: party name, authorized-signature rule, and date rule, laid out on a grid. The party name is editable; the rules are visual chrome and aren’t part of the document text.
Comparing versions
The Compareaction in the topbar opens a read-only structural redline of two versions of a document. Compare mode is the editor with its analysis lenses swapped for the diff lens: the legal terms-of-art, defined-term, and live cross-reference decorations are turned off so the changes stand on their own — section numbering stays, since you need it to read a redline. The body marks each changed clause with its class — added, removed (struck through inline, as a legal redline expects), moved, renumbered (a small chip showing the old → new label), or amended — with the intra-clause edits called out as underline-add / strikethrough-del. A vertical structural ribbon to the left of the paper gives a minimap of the changes; for any moved clause it draws an SVG arc from where the clause used to be to where it landed, with certainty — the durable node id makes that line a fact, not a guess. Hover a ribbon segment to spot its clause in the body; click to jump to it. A right-rail integrity panelsurfaces the findings the body deliberately doesn’t decorate: cross-references that broke or that were relabeled by an auto-update, and defined terms that were renamed or removed (with their orphaned usages flagged). Each finding is clickable and reveals the source clause. A small picker in the bar swaps between sample compare pairs.
Runs of contiguous unchanged sections inside an article collapse by default into a single mono “Nunchanged sections” widget — click to expand, click again to fold. The redline’s value is thechanges; context is one tap away.
02 Reference
Keymap
Structure
| Tab | Demote — push the current article/section deeper into the previous sibling |
| ShiftTab | Promote — lift the current section out of its parent |
| Alt↑ | Move the current peer up among its siblings |
| Alt↓ | Move the current peer down among its siblings |
| Enter | In a heading: at end → into body; mid-heading → split into sibling; at start → empty sibling above |
| ⌘⌫ | Delete the section / article / clause containing the caret. Children go with it. Toast offers Undo. |
| ⌫ | At the start of an empty hierarchy node (blank heading, no body): delete the node. Toast offers Undo. |
| ⌫ | At the start of a non-empty heading: first press selects the whole hierarchy node (heading + body + children) with a soft accent ring; second press deletes it. Click or arrow keys exit the selection. |
| ⌘Enter | From a body paragraph: insert an empty sibling after the current hierarchy node |
Formatting
| ⌘B | Bold · Ctrl+B on Windows/Linux |
| ⌘I | Italic |
| ⌘U | Underline |
| ⌘ShiftD | Define term — wrap the selection as a definition · Ctrl+Shift+D on Windows/Linux |
| ⌘ShiftK | Mark as usage — wrap the selection as a usage of a registered term · Ctrl+Shift+K on Windows/Linux |
| ⌘ShiftR | Insert cross-reference — open the picker for any hierarchy node · Ctrl+Shift+R on Windows/Linux |
| Tab | Accept the live cross-reference suggestion when a chip is showing beside the caret. Plain Tab only; Shift-Tab still promotes the current hierarchy node. · Esc dismisses the suggestion silently. With no suggestion active, Tab falls through to the hierarchy demote behavior. |
| ⌘Shift→ | Next sibling — when caret is on a defined term or term of art, jump to the next occurrence (wraps) · Ctrl+Shift+→ on Windows/Linux |
| ⌘Shift← | Previous sibling — same as ⌘⇧→, in reverse · Ctrl+Shift+← on Windows/Linux |
Find and replace
| ⌘F | Open the find panel · Ctrl+F on Windows/Linux |
| ⌘G | Next match |
| Shift⌘G | Previous match |
| Enter | Next match (focus inside the find input) |
| ShiftEnter | Previous match (focus inside the find input) |
| Esc | Close the find panel and restore focus to the editor |
Outline navigation
| ↑ | Move focus to the previous visible item |
| ↓ | Move focus to the next visible item |
| → | Expand a collapsed item, or move into its first child |
| ← | Collapse an expanded item, or move out to its parent |
| Home | Move focus to the first outline item |
| End | Move focus to the last visible outline item |
| Enter | Navigate the editor to the focused heading |
| A–Z | Type-ahead — jumps to the next heading whose text starts with what you typed |
History
| ⌘Z | Undo |
| Shift⌘Z | Redo |
| ⌘Y | Redo (alternate) |
| ⌘S | Save the current document |
Compare mode
| Alt↑ | Step to the previous change (compare mode only) · Option+↑ on Mac. Steps through reviewable changes — added, removed, moved, amended — and clamps at the first change. Ignored while typing in a field. |
| Alt↓ | Step to the next change (compare mode only) · Option+↓ on Mac. Steps through reviewable changes — added, removed, moved, amended — and clamps at the last change. Ignored while typing in a field. |
Hierarchy vocabulary
The schema enforces exactly five typed levels of legal hierarchy. The same vocabulary is used everywhere — in commands, in issue titles, in the outline rail — so it’s worth learning.
| Level | Depth | Role |
|---|---|---|
| Article | 1 | Top-level chapter. Renders centered with an "ARTICLE N" plate. |
| Section | 2 | Numbered subdivision inside an article. Default scheme: "Section 1." |
| Subsection | 3 | Hanging-label block. Used for grouping clauses. |
| Clause | 4 | Operative paragraph with a parenthetical label. |
| Subclause | 5 | Deepest level. Inner enumeration; further nesting is blocked by the schema. |
Numbering schemes
Numbering is derived from structure, not typed by the author. Two schemes ship today, switchable from the toggle in the toolbar:
- Default
- Plain Arabic numerals. Articles are labelled
Article 1,Article 2; sections areSection 1,Section 2. Deeper levels use decimal nesting. - M&A
- Roman articles and decimal sections.
Article I→Section 1.01→Section 1.02. Subsections and below use lower-case letters and lower-case roman numerals.
Switching schemes does not touch document content. Only the numbering widgets rerender — the same persisted document opens correctly under either scheme.
Version reasons
Every version carries a reason that explains how it was minted. Three reasons exist today:
checkin- A labelled major version minted when the holder chooses Check in. Check-ins are the
N.0rows —1.0,2.0, and so on. The Major filter in the History drawer keeps only these rows. save- A minor version minted on every ⌘S while checked out. These are the
N.Mrows withM > 0— e.g.2.1,2.2. Visible in the All filter only. restore- A minor version minted when you restore a past version. The body comes from the chosen snapshot; the version number continues from where the document is today — history is never rewritten. Restore requires holding the checkout.
Legal-term recognition
The editor recognizes terms from a built-in Legal Terms of Art dictionary — covering both M&A and Master Services Agreement vocabulary — and stains the glyphs themselves with a per-tier accent (no underline): oxblood for hard (statutory) terms, dark mustard for semi-rigid, dark purple for negotiated, dark moss for drafting conventions. When the cursor lands in or beside a recognized term, the same hue brightens a notch so the active match pops without swapping identity. The Legal-rail legend dots and tier labels mirror the resting accent so the rail key is the literal color you see in prose.
The Lens toggle in the topbar controls how much recognition shows through: Off hides all decorations, Dim shows only Hard (statutory) terms and fades the surrounding prose to 80% alpha so defined-term marks and Terms of Art read as the only substance on the page, and On shows all four tiers at full ink. The choice persists per-browser; default is On.
Term Inspector
Hover the pointer over a recognized term and a small card surfaces below it with the term name, tier, plain-language definition, a watch note when one applies, and — if the term is also defined locally via ⌘ShiftD — a Jump to definition in documentlink. The card’s left stripe carries the tier hue; the rest of the card stays ink-on-paper so the page never reads as a Christmas tree.
Moving off the term dismisses the card. Alt-click on a term to pin the card without moving the caret — useful when you need to read the card while moving the pointer elsewhere. Dismiss a pinned card with Esc, by clicking outside, by starting to type, or — on a touch device — by grabbing the handle at the top of the card and flicking it sideways. The same swipe gesture dismisses the cross-reference inspector.
Known gaps in Phase 1
- Defined-term marks ship with a wrap command, a rename command, the Defs rail tab, and phantom detection. Per-doc phantom dismissal, auto-suggest for usages, and the section-gutter coordination still need to land.
- No cross-reference marks yet (Phase 2).
- No axe-core CI gate yet. A11y is verified manually plus a vitest unit test on the heading-level decorations; full pipeline integration is tracked separately.
- No real-time collaboration (Phase 3).
- Tables, images, and footnotes are not preserved when pasted.
- The Redline lens shows changes since checkout and lets you accept or reject them at the word, clause, or whole-document level; an unresolved redline now survives saves and reloads while you stay checked out.