# CONTRACT.md — Slate

What this product does. Every clause has a stable ID and at least one tagged test; `pnpm verify` reports each as satisfied, overridden, or broken. Security, privacy, and operational guarantees are in `BASELINE.md`; a clause here may supersede one of them by naming its `BASE-` ID. `BLOCK-008` supersedes `BASE-INPUT-003`'s download disposition, narrowly and with its own test; no other clause supersedes a baseline clause. Capacity and rate limits are in `seed.json → limits`. Times are p95 at those limits. Clauses marked *Policy* have a default that buyers may replace through the named extension point. There are eight, they are the eight divergences between the products in this family, and each names both profiles and what each one shows and hides. A rule that can be written down is written down instead of becoming a ninth.

## Scope

A knowledge workspace for one business: the family a Notion user and a Confluence user both arrive from. The primary record is a page — a title, an icon, a cover, a byline and a body of blocks — living in a tree inside a container, commented on in place, versioned, shared with named people and groups, and exposed to the anonymous web only by an explicit act. Members organise pages into containers, keep structured records in the same tree, search everything they may read, and ask AI over the same boundary. Slate replaces Notion and Confluence: it imports each product's own export, ships Notion's conventions as its defaults, and reaches Confluence's through the eight policies below. It is one product, not a suite, and the foot of this contract keeps two different statements apart. Never: plan ladders, a vendor product shelf, a commercial marketplace of templates or apps, deep coupling to one issue tracker, a bundled screen recorder, a second workspace per deployment, real-time co-editing and a native mobile application. Not yet, where the foot of the contract names the seam each arrives on and says plainly which three have none: whiteboards, folders, blogs, container calendars, container landing pages, company hubs, team profiles, an activity feed, meeting notes, an assignments-across-containers view, a label filter over a container, rollup and formula evaluation, suggested edits, page locking, and per-agent budgets and model choice.

The words in this contract are the family's, not either product's. A **container** is what Notion calls a teamspace and Confluence a space. A **record** is what Notion calls a page in a database and Confluence an entry. A **page body** is what Notion calls blocks and Confluence elements or macros. A **member** is a person with an account in the workspace; `BASELINE.md` calls the same person an operator, and the two words mean one thing. An edition relabels these words; no clause changes when it does.

## Public landing

- **HOME-001 — Owner identity plate.** The public root path (`/`) shows only the deployment owner's buyer-configured mark, name, and one-line description, plus a sign-in action. It may show a static “Powered by Slate · runeditrun.com” link when the buyer-configured credit toggle is on, which is its default. The landing exposes no workspace records or member identity, makes no marketing claims, and shows no metrics, testimonials, pricing, feature tour, tracking, or third-party resources. Pages published to the anonymous web are served under the published-page prefix and are governed by `WEB-001` to `WEB-007`; the root never lists them.

## Workspace and members

- **WORK-001 — One workspace.** A deployment holds exactly one workspace and every container, page, record and comment belongs to it. Its name and mark are editable in settings and shown in the app header. No interface or endpoint creates a second workspace.
- **WORK-003 — Member management.** Members are invited by email address and removed in settings. A workspace owner may invite any person and remove any member other than the setup member; a member may invite and may remove no one. The member created at setup can be removed only by themselves, and `WORK-007` rejects removing the last workspace owner. No policy value, extension, automation or AI action changes any of this.
- **WORK-006 — Invitations.** An invitation is single-use, can be revoked before it is used, and can no longer be accepted 24 hours after it was sent; an expired invitation is retained, not deleted. Requesting an invitation returns the same response whether or not the address already belongs to a member.
- **WORK-007 — Removal.** Removing a member revokes their sessions and unused invitations immediately, removes their personal grants from every page and container, archives their personal container and its pages (`CONT-005`) rather than exposing or deleting them, leaves each page they owned with no owner and offers reassignment in bulk (`CONT-006`), and keeps their name on existing activity until personal-data erasure anonymises it (`BASE-DATA-003`). Removing the only remaining workspace owner is rejected.
- **WORK-008 — Roles.** A member is a workspace owner or a member. Workspace owners administer the workspace: settings, members, groups, guests, containers they are not in, and the site-wide switches in `CONT-007`. Every other permission comes from a grant on a page or a container (`SHARE-001`, `CONT-009`), never from the role.
- **WORK-009 — Groups.** A workspace owner can create a named group, add and remove members, and use it as a grant principal anywhere a person can be used. Changing a group's membership changes access at once and writes one activity entry, and deleting a group removes its grants without touching the grants of its members.
- **WORK-010 — Guests.** A guest is a person invited to named pages or containers only. A guest is outside the member count and the member list, reaches nothing but what they were granted, and cannot see the container index, the workspace member list, or search results beyond their grants. A container may forbid guests (`CONT-007`).
- **WORK-011 — Workspace settings.** Settings are a shell with its own navigation, holding workspace identity, members, groups, guests, the status vocabulary, published-page and guest switches, connected services, AI, import and export. It is separate from a container's own settings shell (`CONT-004`), and a change in one never silently changes the other.
- **WORK-012 — Theme.** Light, dark and follow-the-system are offered, apply to every operator surface including editors, and persist per member across sessions and devices. The anonymous read view uses the published page's own theme (`WEB-002`), never the reader's member setting.
- **WORK-013 — First run.** A new deployment runs a short wizard that names the workspace, creates the first container and its first page, and ends on an invitation step that can be skipped. It runs once, is never shown to a member who joins later, and creates nothing beyond those three records.
- **WORK-014 — Shortcut reference.** One reference lists every keyboard shortcut and every typed formatting shortcut the editor accepts, is reachable from anywhere in the operator app by a single shortcut, and matches what the editor actually does.
- **WORK-015 — Identity provider.** Sign-in is by the baseline's magic link (`BASE-ACCESS-001`) and, when a workspace owner configures one, by an identity provider through `identity.provider.v1`. A provider sign-in creates no member who was not invited or allow-listed, and an unconfigured or failing provider leaves magic-link sign-in working and says which is unavailable. There is no password, no security question and no account-recovery path, because `BASE-ACCESS-001` stores no password: access to the mailbox is the credential, and the identity provider is the only alternative to it. A member who has lost their mailbox is re-invited at a new address (`WORK-003`), which is not a recovery flow and does not restore their old sessions.

## Editing and saving

- **SAVE-001 — Durable save state.** An editor shows Saving until the write and its activity entry are committed, then shows Saved within 2 seconds of the commit.
- **SAVE-002 — Failed writes.** A failed write leaves the editor in a failed state, never Saved. It keeps the unsent value, names the failure, and offers an explicit retry.
- **SAVE-003 — Revision conflicts.** Every edit is submitted with the revision it began from: the block's revision for a body edit, the page's for a title, property, structure or order change. An edit against a superseded revision is rejected with 409, keeps the local draft, and offers compare, reload, or explicit overwrite. Slate never resolves a conflict by last write wins.
- **SAVE-004 — Network loss.** While the server is unreachable the editor shows Offline and keeps the unsent draft in browser storage as recovery only. A recovered draft is labelled unsaved and never appears in the tree, in lists, in search, or in export as workspace data.
- **SAVE-005 — Operation keys.** Every mutation accepts an operation key. Repeating a key with the same body within 24 hours returns the first response and creates no second record, notification, AI run, publish, import, or activity entry; repeating it with a different body is rejected with 409.
- **SAVE-006 — Concurrent editing without co-editing.** Several members may edit different blocks of one page at the same time and each save succeeds; two edits to one block are resolved by `SAVE-003`, never merged. A page shows who else has it open as an avatar stack and nothing more: there are no shared cursors, no character-by-character streaming, and no automatic merge. An open page refreshes another member's committed blocks within 10 seconds, leaving the reader's own unsaved block untouched.

## The page

- **PAGE-001 — What a page is.** A page has an identifier, a title, an optional icon, an optional cover, an owner, contributors, created and updated times, a container, a position in that container's tree, a body (`BLOCK-001`), a lifecycle state (`LIFE-001`), grants (`SHARE-001`), a revision (`REV-001`) and a set of labels. A label is a short text tag, unique on the page, added and removed by anyone with edit access, shown in the page's details (`PAGE-007`), carried through import (`IMP-003`) and through export and re-import (`PORT-001`, `PORT-002`, `PORT-007`), and never discarded; filtering a container by label is a Confluence-edition contribution and is not in 1.0. A database record is a page (`DB-010`), and every comment thread, revision, decision record and public exposure record names exactly one page.
- **PAGE-002 — Creation.** A member with edit access on the destination can create a page blank or from a template (`TMPL-003`), from the tree, from the global create action, or as a child of an open page. It is created in exactly one container at one position, and its initial lifecycle state follows `LIFE-006`.
- **PAGE-003 — Identity survives everything.** A page keeps its identifier for its lifetime. Renaming it, editing it, moving it within or between containers, archiving, trashing, restoring, publishing and unpublishing never change it, and every link, mention, citation, content query and search result continues to resolve to it (`LINK-001`).
- **PAGE-004 — Move.** A member with edit access on both places can move a page to another position, another parent, or another container, choosing the destination from a searchable picker. Children move with it, grants are re-resolved against the new parent and the change is shown before it is applied, and comments, versions and links are unchanged.
- **PAGE-005 — Duplicate.** Duplicating a page copies its title, icon, cover, body and properties into a new page with a new identifier, optionally including its children. It copies no comment, version, watcher, analytics figure, grant beyond the destination's inheritance, or public link.
- **PAGE-006 — Icon and cover.** A page takes an icon from emoji, a supplied icon set or an upload, and a cover from a gallery, an upload or a link. Both are removable, both render in the tree and in search results where the surface has room, and neither is required.
- **PAGE-007 — Details.** A page's details show its owner, creator, contributors, created and updated times, length, status (`STAT-002`), labels (`PAGE-001`), views (`PAGE-013`) and the pages that link to it. A member with `full` access can reassign the owner, which changes no grant and writes one activity entry.
- **PAGE-008 — Favourite.** A member can favourite a page or a container and unfavourite it. Favourites are personal, are listed in the navigation (`NAV-003`), and are visible to no other member.
- **PAGE-009 — Watch.** A member can watch a page, and watches it implicitly after commenting on it until they stop. A watched page delivers its comments, replies and published changes to that member's inbox (`NOTIF-001`). Watching is personal and a member's own action never notifies them.
- **PAGE-010 — Outline.** A page shows an outline built from its headings that reflects the current body within 2 seconds of an edit, and selecting an entry scrolls to that heading. A page with no headings shows no outline rather than an empty one.
- **PAGE-011 — Present.** Present mode fills the screen, paginates the page by its top-level headings, and shows no navigation, comments or editing affordances. Leaving it returns to the same scroll position.
- **PAGE-012 — Page display options.** Each page carries its own typeface from a fixed set, small-text and full-width toggles, and these are stored on the page and are the same for every reader. They change no stored content and survive export and import.
- **PAGE-013 — Analytics.** A page records total views and unique viewers over time with the members who viewed and edited it, and a container rolls the same figures up over its pages. Both scopes ship and neither is gated by a plan, a role beyond read access to the page, or a policy. A member's own views of their own page are counted and labelled as such, and analytics never expose a viewer a member could not otherwise see.
- **PAGE-014 — Reactions.** A member can add and remove an emoji reaction on a page and on a comment (`COMM-005`). Reactions show who reacted, are not notifications, and are not counted in analytics.

## The page tree

- **TREE-001 — A nested tree in a container.** Every container holds one tree of pages of unlimited depth. A node shows its icon, title and whether it has children, expands and collapses, and remembers its expansion per member. A page has exactly one parent — a page or the container root — and appears in exactly one place.
- **TREE-002 — Reordering and nesting.** A member with edit access can reorder a page among its siblings and re-parent it by dragging or through `PAGE-004`. Order is explicit and stored, is the same for every member, and never changes as a side effect of an edit, a rename or a publish.
- **TREE-003 — Shared with me.** A member's navigation lists pages granted to them individually that sit in containers they are not a member of, with their container named. Removing the grant removes the entry within the same load.
- **TREE-004 — Filter the tree.** Each container's tree carries a filter over page titles that narrows the visible nodes while keeping their ancestors, matches within 200ms of typing, and clears in one action without changing expansion state.
- **TREE-005 — Create in place.** A page can be created at the foot of a tree, as a child from any node's own actions, and from one global create action available on every operator screen. Each names the destination before creating.

## The page body

- **BLOCK-001 — A body of blocks.** A page body is an ordered tree of typed blocks, each with an identifier, a type, content, and optional children. Every block has a drag handle, can be reordered and nested, can be selected with its neighbours, and can be duplicated or deleted. Block identifiers survive editing, moving and export, and an anchored comment (`COMM-001`) points at one.
- **BLOCK-002 — Inline text.** Text in a block carries bold, italic, underline, strikethrough, inline code, colour, links, and mentions, and nothing else. It is stored in one format, rendered under `BASE-INPUT-002`, and round-trips unchanged through page export, workspace export and import.
- **BLOCK-003 — The insert menu.** Typing `/` at the cursor opens an insert menu grouped by kind, filtered by what is typed, navigable and selectable by keyboard alone, and it inserts at the cursor without leaving the editor. Every block type this contract names is reachable from it, and on a touch client the same list is reachable from a labelled control above the keyboard.
- **BLOCK-004 — Markdown shortcuts.** Typed Markdown converts as it is written — headings, lists, quotes, code fences, dividers, bold, italic and inline code — and each conversion is undone by one undo without losing the typed characters. The reference in `WORK-014` lists every shortcut the editor accepts.
- **BLOCK-005 — Mentions.** Typing `@` offers people, pages and dates in one list. A person mention notifies them (`NOTIF-001`) and renders their name; a page mention renders the page's current title and follows renames and moves (`LINK-003`); a date mention renders in the reader's locale.
- **BLOCK-006 — Structured blocks.** The body carries headings, bulleted, numbered and toggle lists, quotes, dividers, callout panels, expandable sections, tables with header rows, and code blocks with a language and copy action. Each survives export and import and each has a Markdown or import equivalent named in `IMP-002` and `IMP-003`.
- **BLOCK-007 — Task items.** A checkable item in the body toggles for anyone with edit access, records who completed it and when, and is counted where the page shows a completion figure. It is a block, not a record in a database, and it never appears in a database view.
- **BLOCK-008 — Media blocks.** Images, video, audio and file attachments are inserted by upload, by link, or from a configured stock source, each with an optional caption and an alt text field, and stored under `BASE-INPUT-003`. This clause supersedes `BASE-INPUT-003` on disposition alone. A file requested for display — an image, video or audio block, a page icon or cover (`PAGE-006`), or either of those behind a published page (`WEB-003`) — is served inline with its declared content type; every other request for a stored file, including every file attachment and every direct request for a media file, is served with a download disposition. Both dispositions send the declared content type and `X-Content-Type-Options: nosniff`, and an SVG is sanitised under `BASE-INPUT-002` before it is served inline. Disposition changes nothing about access: a media file is readable only by a session that may read its page, or through that page's public link (`WEB-003`).
- **BLOCK-009 — Link cards.** A pasted URL becomes a link card showing the target's title, description and icon, and can be switched between a plain link, an inline card and a block card without retyping it. Fetching a preview goes through `link.preview.v1` and issues a bodyless GET to public HTTPS origins only, sends no session, credential or member identity, rejects local, private, link-local and cloud-metadata addresses before the first request and before each redirect, and enforces the redirect, byte, time and media-type limits in `seed.json → limits`. A refused or failed fetch leaves the plain link and says so; the URL is stored exactly as pasted either way.
- **BLOCK-010 — Content queries.** A body can hold a live list of pages defined by container, ancestor, type, status, contributor and date, ordered and limited by the author. It re-runs when the page is opened, shows when it last ran, shows each reader only the pages that reader may read, and never renders a draft (`LIFE-002`). A query whose source is deleted shows that, not an empty list.

## Page lifecycle

- **LIFE-001 — Lifecycle states.** A page is `draft`, `scheduled`, `live`, `live-with-unpublished-changes` or `archived`. It always has a working revision and has a published revision in every state but `draft`; `live-with-unpublished-changes` means both exist and differ. Every transition records the actor and the time, and every state exists in the stored model whichever value `LIFE-006` carries.
- **LIFE-002 — A draft is private.** While a page is `draft` it is absent from search, from content queries, from the inbox, from analytics, from AI grounding, from every administrative and export listing, and from the tree of every member it has not been shared with. Its body is readable only by its author and by the principals in its own grant list: sharing a draft, by link or by address, creates a grant under `SHARE-001` and `SHARE-006`, that grant requires a session like every other, it is shown in the draft's grant list, and revoking it ends access under `SHARE-007`. There is no readable address for a draft that carries no grant, a draft has no general access grant under either value of `SHARE-005` (`SHARE-004`), and a draft cannot be published to the anonymous web (`WEB-001`). No role, policy value, container setting, workspace owner action, automation or extension defeats this. `LIFE-006` decides whether a draft state arises, never whether this clause holds.
- **LIFE-003 — Publish.** Publishing takes a location, a general access setting (`SHARE-004`), an optional version comment, an optional schedule and an optional publish-as attribution, shows a preview of what readers will see, and on confirmation makes the page visible in its container's tree, in search and in content queries, and writes one version with origin `publish` (`REV-002`).
- **LIFE-004 — Scheduled publish.** A publish set for a future time leaves the page `scheduled`, shows that time wherever the page appears to its author, and publishes at that time without a further action. Cancelling before it fires returns the page to `draft` and publishes nothing; a schedule that fails to fire leaves the page `scheduled` and reports the failure under `BASE-OPS-004`.
- **LIFE-005 — Unpublished changes.** Editing a `live` page changes what readers see only when it is republished: every reader other than the editors sees the published revision, and the page shows that it has unpublished changes to anyone who may edit it. Discarding them restores the published revision as the working revision and deletes no version.
- **LIFE-006 — Lifecycle profile.** *Policy: `page.lifecycle.v1`; default: `always-live`, following Notion. Under `always-live` a new page is live from creation and the product hides the publish action and its modal, close and discard draft, the draft banner, the draft chip in the tree, the scheduled state, the unpublished-changes state, the version-comment field and the publish-as selector; the page's own share panel (`SHARE-001`) is how it becomes visible to others. Under `draft-then-publish` a new page appears only in its author's tree with a draft chip, nothing is hidden, and `LIFE-003` to `LIFE-005` are the working path. Both values store every state in `LIFE-001`, so a workspace can be switched either way without a migration and an imported draft is never published on arrival.*
- **LIFE-007 — Discarding a draft.** The author of a `draft` page can discard it, which moves it to trash (`ARCH-005`) rather than deleting it, and can close it, which leaves it a draft. Neither action notifies anyone or leaves a trace in another member's tree.

## Sharing and access

- **SHARE-001 — Grants.** A page carries an ordered list of grants. Each grant names a principal — a person, a group, a container or the workspace — a level from `full`, `edit`, `comment` and `view`, and an optional expiry. The panel that edits them shows every effective grant, whether it is inherited or set here, and where an inherited one comes from.
- **SHARE-002 — What the levels mean.** `full` may read, edit, comment, share and change access; `edit` may read, edit and comment; `comment` may read and comment; `view` may read only. Every server operation checks the level for the acting member (`BASE-ACCESS-004`), and no interface offers an action the level does not carry.
- **SHARE-003 — Inheritance.** A page inherits its parent's grants, and a child added later inherits the parent's grants at that moment. A page may add grants or be restricted, and a restriction is shown on the page and on every descendant it affects. Removing a restriction restores inheritance rather than copying the parent's grants.
- **SHARE-004 — General access.** At most one grant on a page has the container or the workspace as its principal; that is the general access grant. A page that carries none is restricted to its named grants, and a page in a personal container (`CONT-005`) carries none under either value of `SHARE-005`. Changing it is one action and is recorded in activity.
- **SHARE-005 — Access profile.** *Policy: `page.access.v1`; default: `workspace-general` with all four levels, following Notion. `workspace-general` makes the workspace the general principal and shows the four-level ladder on each grant. `container-general` makes the container the general principal, labels that row with the container's name, and hides nothing else. Removing a level from the list hides it everywhere, including the comment affordance when `comment` is removed. A page imported with a container-wide general grant is created with a container principal under either value, never widened to the workspace. Switching the value relabels the general access row and rewrites no grant: no value widens an existing page's general principal, and no page gains a general grant it did not already carry.*
- **SHARE-006 — Sharing with a person who is not a member.** Granting access to an address that belongs to no member creates a guest invitation (`WORK-010`, `MAIL-001`) and the grant takes effect when it is accepted. Until then the page shows the pending grant, and revoking it cancels the invitation.
- **SHARE-007 — Access changes take effect at once.** Removing or narrowing a grant applies to the next request from every existing session, closes an open editor with an explanation rather than silently discarding the writer's unsaved text, and removes the page from that member's search results, tree, inbox items and AI answers within 5 seconds.
- **SHARE-008 — Expiring grants.** A grant may carry an expiry time. When it passes, access ends under `SHARE-007`, the grant is shown as expired rather than deleted, and re-granting is one action.

## Public links

- **WEB-001 — Anonymous access takes two acts.** A page is readable without signing in only when a member with `full` access on it publishes it (`WEB-002`) and only while its container allows it. Each of the container's three switches (`CONT-007`) vetoes its own act and no other. While public links are forbidden, publishing a page in that container is rejected and every existing public link on its pages stops resolving at once. While anonymous access is forbidden, a published page in that container resolves only for a signed-in member who may read it. While guests are forbidden, a guest invitation to that container or to a page in it is rejected and existing guest access to it ends under `SHARE-007`. Each veto is not gated by a plan or a role beyond container administration, is not a policy value, and no extension, automation or AI action may bypass it.
- **WEB-002 — What a published page carries.** Publishing writes a public exposure record holding the host, a slug the publisher may edit, a search-engine indexing flag, a theme, the set of header controls the page shows, whether readers may duplicate it, and the time it was revoked if it was. The slug is stored, never derived, so an imported public address is preserved exactly; a slug already in use is rejected.
- **WEB-003 — The anonymous read view.** A published page renders its published revision and nothing else: no navigation, no editing, no comments, no member names beyond the byline the page itself shows, and no other page unless that page is itself published. It sets no cookie, loads nothing from a third-party domain, and sends no visitor data before an interaction (`BASE-PUBLIC-001`), and it counts a view without identifying the viewer (`PAGE-013`).
- **WEB-004 — Children are not published by inheritance.** Publishing one page publishes only that page unless the publisher explicitly includes its children, and a child added later is not published until someone publishes it. A link from a published page to an unpublished one is rendered as text, not as a link that would return a sign-in page.
- **WEB-005 — Indexing is off until it is chosen.** Search-engine indexing is off for a newly published page: the response and the served document tell crawlers not to index it until a member turns indexing on, and turning it off again restores that state within one request.
- **WEB-006 — Unpublish.** Unpublishing stops the address resolving within 5 seconds, returns a content-free page saying the link is no longer available, and keeps the slug reserved for that page so republishing restores the same address. It deletes no version and changes no grant.
- **WEB-007 — Public-link profile.** *Policy: `page.public-access.v1`; default: `site`, following Notion. `site` shows the host and slug fields, the theme, the header controls, the duplicate-as-template switch, social preview settings and the live-site banner over the page. `link` hides all of those and shows a single toggle, the resulting URL, the indexing switch and unpublish. Both values store the whole record in `WEB-002`, so a workspace switched to `link` keeps the slug and theme an imported or previously published page already had.*

## Versions

- **REV-001 — What a revision is.** Every page, record, container, comment, template and decision record (`DEC-001`) carries a revision identifier that changes on every committed edit and is never reused. A page revision is immutable: it holds the body and properties as committed, its author, its time, its origin of `autosave` or `publish`, and a content hash, being SHA-256 of the revision's extracted text after Unicode NFC normalisation and whitespace collapse. A retried write whose hash matches the latest revision commits nothing. Wherever another clause names a revision, it means this identifier.
- **REV-002 — History.** Each of those records has a history listing its revisions newest first with the actor, the time, the origin, and, where `REV-007` assigns them, an ordinal and a label. History is readable by anyone who may read the record and is never editable.
- **REV-003 — Restore.** A member with edit access can restore a previous revision. Restoring writes a new revision holding the earlier content, records who restored which revision in activity, and changes no link, comment anchor, child page, grant or public link.
- **REV-004 — Compare.** Any two revisions of one page can be compared, showing added, removed and changed blocks in place. Compare is available under every value of `REV-007`.
- **REV-005 — A citation resolves to its revision.** A link, mention or AI citation that names a revision opens that revision with its content as committed, even after the page has changed, and says that a newer revision exists. The reader's access is checked against the page as it is now: a member who may not read the page today gets `LINK-004` and no historical content, an anonymous reader gets nothing unless the page is published (`WEB-001`), and no grant that has since been removed or expired reopens a revision. When the page has been erased it opens a content-free tombstone (`LINK-002`) carrying no body, quote or personal metadata.
- **REV-006 — Deleting a version.** A member with `full` access can delete one intermediate version, which removes its content and leaves a numbered tombstone in history naming who deleted it and when. The current revision and the published revision cannot be deleted, and deletion is offered only where `REV-007` says so.
- **REV-007 — Version profile.** *Policy: `page.version.v1`; default: `autosnapshot`, following Notion. `autosnapshot` writes a revision on save, assigns no ordinal or label, and hides the version and comment columns, the version-comment field and version deletion, keeping selection, compare, restore and difference summaries. `labelled` numbers each published revision, carries the version comment taken at publish and the page's status at that moment, and hides nothing. Ordinals come from publish events, so a workspace with `page.lifecycle.v1` at `always-live` and this policy at `labelled` numbers nothing until a page is published; the model stores the ordinal and label fields under both values, so an imported numbered history is never discarded.*

## Page status and verification

- **STAT-001 — The status vocabulary.** The workspace holds a list of status entries, each with a key, a label, a colour and an optional verification requirement. It ships seeded with Rough draft, In progress, Ready for review and Verified under every value of `STAT-005`, and a workspace owner can add, rename, recolour and retire entries. Retiring an entry keeps it on the pages that carry it and offers a bulk change (`CONT-006`).
- **STAT-002 — A page's status.** A page carries at most one status. Setting or clearing it is one action for anyone with edit access, writes one activity entry, and makes the page findable by that status in search (`SEARCH-003`) and in content queries (`BLOCK-010`).
- **STAT-003 — Verification.** Setting a status entry that requires verification records the verifying member as its owner and an expiry of 7, 30 or 90 days, a chosen date, or none, and can be set only by a member who has opened the exact revision they are attesting to. The badge shows the owner, the expiry and the revision verified, and editing the page after verification shows that the verified revision is not the current one.
- **STAT-004 — Verification lapses.** When an expiry passes, the page shows the status as lapsed rather than verified, notifies the owner once (`NOTIF-001`), and keeps the original verification in history. Nothing renews it automatically, no AI action renews it (`AI-011`), and a lapsed verification never reads as current in search results, content queries, AI answers or exports.
- **STAT-005 — Status profile.** *Policy: `page.status.v1`; default: `verification-only`, following Notion. `verification-only` exposes only the entries that require verification, hides the colour picker and the custom-status field, offers the control from the title's own hover row, and renders the chip beside the breadcrumb title. `open-vocabulary` exposes every entry, shows the colour picker and a custom label of at most twenty characters, and renders the chip on the byline between the author and the reading time. Both values store the whole vocabulary, so switching exposes or hides entries and rewrites no page.*

## Comments

- **COMM-001 — Two kinds of thread.** A member with `comment` access or more can open an inline thread anchored to a selection in a block, and a page thread about the page as a whole. A thread is chronological, is Open or Resolved, and records each comment's author and time. Both kinds are threads; only their anchor differs.
- **COMM-002 — Editing and deletion.** A member can edit or delete only their own comments. An edited comment shows the time it was last edited, a deleted comment leaves a tombstone in place, and both write an activity entry.
- **COMM-003 — Mentions.** Mentioning a member in a comment creates one unread notification (`NOTIF-001`) linked to that comment, and mentions no one who cannot read the page. Editing the comment notifies only a member the comment did not already mention.
- **COMM-004 — Anchors survive editing.** An inline thread follows its text through edits, splits and moves within the page. When its text is deleted the thread becomes orphaned rather than lost: it stays in the discussions panel, names the revision it was anchored in, and opens that revision (`REV-005`).
- **COMM-005 — Reactions.** A member can add and remove an emoji reaction on any comment they can read. Reactions show who reacted and never notify.
- **COMM-006 — Discussions panel.** One panel lists every thread on the page, filtered by kind, status and person, with an action that resolves all open threads. It shows unresolved threads whose anchor is orphaned, and selecting a thread scrolls to its anchor.
- **COMM-007 — Resolve.** Any member with `comment` access can resolve or reopen a thread. A resolved thread is hidden from the page body, stays readable in the panel and in search, and keeps its comments and reactions.

## Archive, trash and deletion

- **ARCH-001 — Archive.** A page, a container or a database can be archived. Archiving records the actor, the time, the prior location and an optional reason, and archives the record's descendants with it.
- **ARCH-002 — What archived means.** An archived record is read-only, is absent from the tree, from default search (`SEARCH-009`), from content queries and from AI grounding, and its existing links, mentions and citations still resolve. It is discoverable by the archived filter, and its comments, versions and grants are unchanged.
- **ARCH-003 — Restore from archive.** Restoring returns a record to its prior parent and position with the same identifiers, revisions, comments, grants and status. Where the prior parent is gone, the member chooses a destination before the restore completes.
- **ARCH-004 — Archive is not deletion.** Archiving deletes no data. Permanent deletion is a separate confirmed action governed by `BASE-DATA-002`, `BASE-DATA-003` and `ARCH-006`.
- **ARCH-005 — Trash.** Deleting a page moves it and its descendants to the trash, where they are listed with the deleting member, the time and the prior location, searchable and filterable within the trash, and restorable to their prior place. Nothing leaves the trash on a timer (`BASE-DATA-002`), and a page in the trash is absent from every surface but the trash itself.
- **ARCH-006 — Permanent deletion.** Emptying the trash, or permanently deleting one record in it, is explicit and confirmed, names what will be destroyed, and removes the body, every revision, uploaded files, comments, analytics rows and AI grounding within the cascade deadline in `BASE-DATA-003`. Every link to it then resolves to a content-free tombstone (`LINK-002`), and activity entries survive with their references anonymised.

## Containers

- **CONT-001 — What a container is.** A container has an identifier, an optional key, an icon, a name, a description, owners, a visibility of `default`, `open`, `closed`, `private` or `personal`, a grant list shaped like a page's, and external-access switches (`CONT-007`). It holds one page tree (`TREE-001`) and the databases in that tree.
- **CONT-002 — Creating a container.** A workspace owner or, where the workspace allows it, any member can create a container from a form taking an icon, a name, a description, a visibility and an optional key, and a purpose that sets the form's defaults. A purpose supplies default values only; it creates no second kind of container and nothing about the container is unreachable afterwards.
- **CONT-003 — The container index.** One index lists every container the member may see with its name, description, access and member count, filtered by membership, visibility and whether it is personal, and opens each one. Containers a member cannot see are neither listed nor counted.
- **CONT-004 — Container settings.** Each container has its own settings shell holding its identity, members and grants, external-access switches, templates, trash and bulk tools. It is separate from workspace settings (`WORK-011`) and a container administrator reaches only their own container's.
- **CONT-005 — Personal containers.** Every member has one personal container that no other member can be granted access to; its pages are theirs alone until they are moved or shared to another container. Deleting the member archives it under `WORK-007` rather than exposing it.
- **CONT-006 — Bulk actions.** A member can select several pages, in a tree or in a container's content list, and archive, trash, move, change owner or change status for all of them in one action. Each selected page records its own change with its own activity entry, each is checked against the acting member's access, and a partial failure names the pages that failed and applies the rest.
- **CONT-007 — External-access switches.** A container carries three switches — guests, public links and anonymous access — each `allowed` or `forbidden`. A workspace owner sets a workspace-wide value and a container administrator may only narrow it. Forbidding takes effect at once under `WEB-001` and `SHARE-007`. These switches carry no plan, tier or upgrade gate.
- **CONT-008 — Container access profile.** *Policy: `container.access.v1`; default: `class-led`, following Notion. `class-led` leads with the visibility values in `CONT-001`, each stating who may see and join, and folds the member, group and guest lists behind one manage-access view. `grant-led` leads with tabbed lists of members, groups and guests and an external-access card, and hides the visibility dropdown. Neither value hides the switches in `CONT-007`. Both store visibility and the grant list, so switching relabels the surface and rewrites no grant.*
- **CONT-009 — Container grants.** A container's grants use the principals and levels of `SHARE-001` and `SHARE-002`, are the source of every page's inherited access, and are editable only by a container administrator or a workspace owner. Every change is recorded in activity and takes effect under `SHARE-007`.

## Structured records

- **DB-001 — A database.** A database is a node in the page tree with a name, an icon, typed fields and records. It is created, moved, archived and trashed like a page, and its access follows `SHARE-001`.
- **DB-002 — Field types.** A field is one of: text, number, checkbox, date, select, multi-select, status, person, file, URL, email, phone, place, relation to another database, rollup over a relation, formula, created and updated metadata, and identifier. Each has a defined empty value, a defined sort order and a defined import and export representation, and changing a field's type either converts every value or is rejected naming what would be lost. Rollup and formula are stored, imported, exported and displayed in 1.0 and are not evaluated: an imported rollup or formula field keeps its configuration and the values the export carried, shows on the field that it is not being recomputed, and is never presented as current. Evaluating them is a Notion-edition contribution on `database.field-type.v1`. Relation ships evaluated, because both incumbents have it and Confluence's Page, Jira and Other-Database fields land on it at import; neither incumbent's evidenced field list has a rollup or a formula.
- **DB-003 — Views.** A database has several named views over one set of records. A view is saved for everyone who can see the database, is renamed, duplicated and deleted by anyone with edit access, and never changes the records it shows.
- **DB-004 — Layouts.** A view is a table, a board, a list, a gallery, a calendar or a timeline. A board or calendar requires the field it groups or dates by, and offers to create it rather than failing when it is missing.
- **DB-005 — Filter and sort.** A view carries filter rules over any field, combined with and or or, and an ordered list of sorts. Both are stored on the view, are shown in force above the records, and are cleared in one action.
- **DB-006 — Group.** A view groups its records by a select, status, person, checkbox or date field, shows the count in each group, allows a record to be moved between groups where the field permits it, and hides empty groups on request.
- **DB-007 — Calculations.** Each column in a table view carries a calculation under it — count, filled, empty, sum, average, minimum, maximum, range or percent — computed over the view's filtered records and updated within 2 seconds of a change.
- **DB-008 — Charts.** A chart is built over one view, showing counts or a numeric field aggregated by a grouping field, and can be embedded in a page (`DB-011`). It reads the same filtered records the view shows and states which view it reads.
- **DB-009 — Lock structure.** A database's structure can be locked, which rejects field, view, filter, sort and layout changes while allowing record edits, names who locked it, and is unlocked by anyone with `full` access.
- **DB-010 — A record is a page.** Every record has an identifier that is also a page identifier, a field map, an optional body of blocks, an optional icon and an optional position in the tree. *Policy: `record.page-parity.v1`; default: `record-is-page`, following Notion. `record-is-page` shows the per-record icon, opens a record as a side peek, a centre peek or a full page, offers its body, and lists it in the tree and in page search. `record-is-row` opens a record as a field list with a print action, and hides the icon, the body area, the tree position and the record from page search. The body is stored under both values and is never dropped, so a workspace can be switched back and an imported record with a body keeps it.*
- **DB-011 — A view inside a page.** A view can be embedded in a page body and stays live: it shows each reader only the records that reader may read, reflects a change to the source within 5 seconds, and carries its own filters and sorts without changing the source view.
- **DB-012 — Record detail.** Opening a record shows its fields in a defined order with inline editing, its comments and its history (`REV-002`), and, where `DB-010` exposes it, its body. A field a member may not edit is shown and not editable rather than hidden.

## Automation

- **AUTO-001 — A rule.** A rule has a name, a scope, a trigger, ordered conditions, an optional branch over related records, ordered actions, and an actor. Triggers are record and page events and a schedule; actions edit fields, create pages and records, move and archive them, add comments, and send a notification through a configured adapter.
- **AUTO-002 — A rule acts as its actor.** A rule performs only what its actor could perform by hand: every action is checked against that member's access at the moment it runs, and an action that would exceed it is skipped and logged rather than performed. A rule cannot publish or unpublish a page, change grants or external-access switches, verify a page, permanently delete anything, or manage members.
- **AUTO-003 — Run log.** Every rule keeps a per-run log with the trigger, the actor, the records touched, the time and one of the states configuration-changed, queued, running, success, no-action, condition-not-met, throttled, error, failure and disabled. A failed run is visible without a search (`BASE-OPS-004`), and a rule that fails repeatedly is disabled and says so.
- **AUTO-004 — Loops are bounded.** A rule never triggers itself, a chain of rules stops at the depth in `seed.json → limits`, and a run that would exceed the workspace's rate limit is throttled and logged rather than dropped silently.
- **AUTO-005 — Automation profile.** *Policy: `automation.scope.v1`; default: `source`, following Notion. `source` creates rules from a database's own toolbar, scopes them to that database, shows one When-and-Do form, scopes the run log to that database, and hides the scope selector, the site option, the branch rail and the cross-container rule list. `container` and `site` create rules from a rule list, expose the scope selector and the branch rail, and hide nothing from each other. The scope field and the run log are stored under every value, so an imported container or site rule keeps its scope and its history.*

## Search

- **SEARCH-001 — Global search.** One search covers page titles and retained body text, record field values, container names and descriptions, comments and templates, and returns the first page of ranked results within 500ms with the total number of matches and an ordering stable across pages.
- **SEARCH-002 — Results.** Each result names its kind, shows the matched text in context, and opens the record with the match highlighted. A quoted query matches only records whose retained text contains that exact phrase.
- **SEARCH-003 — Filters.** Results are narrowed by kind, container, contributor, status, updated date, and whether archived or trashed records are included, without changing the query text, and the filters in force are shown with the results.
- **SEARCH-004 — Freshness.** A committed create, edit, publish, move, archive, restore or delete is reflected in results within 5 seconds. An import is not complete until its records are searchable (`PORT-004`), and content removed under `ARCH-006` stops matching within the cascade deadline in `BASE-DATA-003`.
- **SEARCH-005 — What search never shows.** Search returns only what the searching member may read: no page they lack a grant for, no other member's draft (`LIFE-002`), nothing in another member's personal container (`CONT-005`), and no title, snippet, facet count or result total that would reveal the existence of any of them. Access is the only rule that removes a record from search outright; every other exclusion is a default the member can undo (`SEARCH-009`).
- **SEARCH-006 — Search in one container.** Search can be scoped to one container from that container's own surface, keeps every filter in `SEARCH-003`, and says which container it is scoped to.
- **SEARCH-007 — No results.** When nothing matches, search restates the query and every applied filter, offers one action that clears the filters and one that creates a page with that title, and shows no invented result or answer.
- **SEARCH-008 — Search without AI.** With the AI provider unconfigured, disabled, failing or over budget, ranked search and every filter still return within 500ms with no error, and the generated answer (`AI-015`) is absent rather than empty.
- **SEARCH-009 — Default visibility.** Search without the archived or trashed filter (`SEARCH-003`) excludes archived and trashed records and nothing else. This is the only rule that hides a record the searching member may read, and every record it hides stays reachable by applying that filter and through every existing link, mention and citation (`LINK-001`, `LINK-003`).

## Personal surfaces and navigation

- **NAV-001 — Home.** A member's home shows their recent pages, their favourites and the containers they belong to, each entry opening the record. It shows nothing the member may not read and no workspace-wide activity feed.
- **NAV-002 — Recents.** Recently opened pages are listed per member, newest first and grouped by day, and a page appears there for no one but the member who opened it.
- **NAV-003 — Favourites.** Favourited pages and containers (`PAGE-008`) are listed in the navigation, reorderable by the member, and removed from the list in one action.
- **NAV-004 — One navigation.** The operator app has one sidebar holding the global destinations, the personal sections and the container trees, collapsible to leave the page full width. Its order and which entries appear come from workspace configuration and from the navigation slot (`workspace.navigation.after.v1`); changing any policy value in this contract changes no entry in it and no entry's position. A container's own navigation nests inside it rather than replacing it.
- **NAV-005 — Global create.** One create action is available from every operator screen and creates a page, a database, a container or a page from a template, naming the destination before it creates.
- **NAV-006 — The touch client edits.** Every operator surface in this contract works at phone width on a touch device, and none of them is read-only there: a member can create and edit a page and its blocks, comment, resolve, publish, share, and use every database layout. Where a gesture cannot carry a desktop interaction, the touch form is named in the same clause — the insert menu from a labelled control above the keyboard (`BLOCK-003`), reordering by a drag handle rather than a hover target (`BLOCK-001`). Slate is one responsive application; a native mobile application is a non-goal.

## Notifications

- **NOTIF-001 — What notifies.** A member is notified in the app when they are mentioned, when someone comments or replies on a page they watch or authored, when a page they watch is published or republished, when a verification they own lapses (`STAT-004`), when they are granted access to a page or container, and when an AI run they started needs review or failed. Their own action never notifies them, and nothing notifies anyone about a record they may not read.
- **NOTIF-002 — Inbox.** Notifications are listed newest first, grouped by period, with an unread count and separate views for those addressed to the member and those from watched pages. Opening one marks it read and opens the referenced record at the referenced comment; a member can mark all read and archive read notifications.
- **NOTIF-003 — Chat notifications.** A workspace owner can connect one chat destination through `chat.notifier.v1` and choose which events reach it. A message carries the event, the actor, the page title and a link, never the page body, and the link still requires access. The connection shows its last delivery and its last failure, retries only transient transport failures, and stops sending the moment it is disconnected; its credential is encrypted with `CONNECTOR_CREDENTIALS_KEY`, returned by no API response, and destroyed on disconnect. A deployment with a chat destination connected and that variable missing fails at startup naming it (`BASE-SECRET-002`) rather than running with the connection off. An expired or revoked authorisation marks the connection as needing attention, offers a reconnect action, and stops sending without deleting the connection, its event selection or its delivery history.

## Mail

- **MAIL-001 — Outbound only, two kinds.** Slate sends mail only to deliver a sign-in link (`BASE-ACCESS-001`) and an invitation — a member invitation (`WORK-006`) or a guest invitation (`SHARE-006`) — from the configured sender address through `mail.sender.v1`. No product event sends mail, and Slate accepts no inbound mail: a reply to that address creates and changes nothing.
- **MAIL-002 — Delivery failure.** A send that fails is retried up to 3 times over 5 minutes. A send that still fails is recorded with the provider's error and shown to a person — on the sign-in screen for a sign-in link, in settings for an invitation — and is never reported as sent.

## Activity

- **ACT-001 — Durable timeline.** Activity is append-only. Each entry records the actor or the feature that acted, the action, the target record, the time, and the operation key, automation run or AI run that caused it. A mutation and its activity entry commit together or not at all.
- **ACT-002 — Coverage.** Activity records page creation, body and property changes, publish and unpublish, moves, archive, trash, restore and permanent deletion, version restores and deletions, status and verification changes, grant and external-access changes, comment activity, container and group changes, database structure changes, automation runs, AI runs and their acceptance or rejection, member invitation and removal, import and export.
- **ACT-003 — Retrieval.** Members can filter activity by actor, action, record kind, container and date range, with newest-first pagination that stays stable while new entries arrive, and see only entries about records they may read.
- **ACT-004 — Context.** Opening an activity entry opens the record it describes together with the comment thread, revision or grant it references, and changes no entry.
- **ACT-005 — Retained explanation.** No member can edit or delete an activity entry. When a described record is erased the entry remains, naming the record kind and time, with personal references anonymised as `BASE-DATA-003` requires.

## Templates

- **TMPL-001 — Gallery.** Members can search templates by title and description and filter them by category. A search with no match shows "No templates match this search."
- **TMPL-002 — Preview.** A template preview shows the page it produces, including its body, properties and any child pages, before it is used. A figure such as a team size or a duration appears only when the template's author wrote one; Slate never estimates, infers, or displays a placeholder for either.
- **TMPL-003 — Snapshot on use.** Using a template creates a page holding a copy of its body, properties and children in the destination the member chose. Editing, archiving or deleting the template afterwards changes no page already created from it.
- **TMPL-004 — Custom templates.** Members can create, edit, archive, restore and delete templates. Templates shipped with Slate cannot be edited or deleted and can be duplicated into an editable copy.
- **TMPL-005 — Save a page as a template.** Saving a page as a template copies its body, its properties and, on request, its children. It copies no comment, version, grant, watcher, analytics figure, status or public link.
- **TMPL-006 — Where a template lives.** A template belongs to the workspace or to one container. A container's templates are offered first inside that container and are not offered elsewhere, and moving a template between the two is one action.

## Import

- **IMP-001 — Import jobs.** An import shows Queued, Running, Completed or Failed with counts of the records created, and produces a report naming every page, database, record, file and version created and every construct it did not map. It is idempotent by operation key (`SAVE-005`), reaches Completed only under `PORT-004`, and a failed import leaves no partially created tree: it is rolled back and can be retried. The source archive is retained with the job until a member deletes it, so nothing that failed to map is unrecoverable.
- **IMP-002 — Notion import.** Slate imports a Notion workspace export in its Markdown and CSV form: pages become pages with their tree and titles, icons and covers where the export carries them, page bodies become blocks of the equivalent types, links between exported pages resolve to the imported pages, CSV databases become databases with typed fields and records, and files become media blocks. Every page arrives live, and the report names what did not map. Its test fixture is an export produced by Notion itself, committed with the test; a fixture written by hand does not satisfy this clause, because the construct-by-construct mapping is only tested against what the incumbent actually emits.
- **IMP-003 — Confluence import.** Slate imports a Confluence space export in whichever form the product emits — Confluence Cloud's CSV table export, where `content.csv` carries the tree and the version ledger and `bodycontent.csv` carries storage-format bodies, or the single `entities.xml` a Server or Data Center export writes — and single documents in the forms each editor exports: Word's `.docx`, and a Google Docs document downloaded as `.docx` or as zipped HTML, Google Docs having no file form of its own. Pages become pages with their tree, their labels (`PAGE-001`), their versions with their comments and ordinals where the export carries them, their status, and their published state; page and attachment links resolve to the imported records; and named user and group restrictions remain named grants. A general grant is container-scoped, never workspace-wide (`SHARE-005`), and a personal container carries no general grant (`SHARE-004`). The report names what did not map, including whiteboards, blogs, calendars and folders, and a folder is imported as a page with an empty body so its tree survives. Its test fixtures are a space export produced by Confluence itself and one document downloaded from each of Word and Google Docs, committed with the test; a fixture written by hand does not satisfy this clause.
- **IMP-004 — Import safety.** Imported markup, documents and attachments are validated and sanitised under `BASE-INPUT-001` to `BASE-INPUT-003`. An import fetches nothing from the network: media comes from the archive, and a link to a file the archive does not contain is imported as a link and named in the report.
- **IMP-005 — Importing into a page.** A member can import a Markdown, HTML, Word, PDF, EPUB, plain text or CSV document into a page or as a set of pages, mapping its headings to blocks and its tables to tables. The same report and the same safety rules apply.

## Portability

- **PORT-001 — Export contents.** An export contains workspace settings; members, groups and guests; every container, page, database, record, template, comment thread, notification, automation rule, AI run and activity entry as JSON, with each record's revision history and grants; and every uploaded file in its original form.
- **PORT-002 — Referential restore.** Importing an archive into an empty deployment restores identifiers, revisions, tree order, grants, links, mentions, comment anchors, public-link slugs and files, so every link from the source deployment resolves to the same record.
- **PORT-003 — Visible jobs.** Export and import show Queued, Running, Completed or Failed with record counts and, on failure, a report naming what failed. A failed job is never shown as complete.
- **PORT-004 — Search rebuild.** An import is not Completed until search has been rebuilt over the imported records and they satisfy `SEARCH-001` to `SEARCH-006`.
- **PORT-005 — Access boundary.** An export excludes sessions, sign-in links, pending invitations, stored operation-key responses and provider secrets. An import activates only the member named by `SETUP_OWNER_EMAIL`; every other imported member is an inactive historical actor until invited, and no imported page is published to the anonymous web until a member publishes it.
- **PORT-006 — Atomic import.** An import stages records and files outside the live namespace, validates the whole archive, and publishes it in one step. A failed import leaves the deployment empty and can be retried.
- **PORT-007 — Exporting a page or a container.** A member can export one page, a page with its descendants, or a whole container as Markdown, HTML or PDF, with records as CSV, in one archive with its media. The export is readable without Slate, and re-importing it under `IMP-005` reproduces the tree.

## References and links

- **LINK-001 — Permanent URLs.** Every page, page revision, container, database, record, comment thread and template has a permanent URL. Renaming a record, moving it, changing its container, archiving it or restoring it never changes that URL, and every existing link, mention, citation, content query and search result continues to resolve to it.
- **LINK-002 — Tombstones.** A link to a deleted or erased record resolves to a tombstone naming the record kind and the time it was deleted, not an error page or an empty view, and exposes no erased content or personal metadata.
- **LINK-003 — References follow their target.** A mention or link to another record renders that record's current title and state, follows renames and moves, and is labelled archived, trashed or deleted rather than removed when its target is.
- **LINK-004 — Links respect access.** A mention or link to a record the reader may not open shows that a linked record exists and that access is needed, and shows no title, snippet, icon or author. Requesting access is one action that notifies a member who can grant it.

## AI

- **AI-001 — Enablement.** AI is off until an AI provider is configured (`ai.provider.v1`) and a workspace owner turns AI on in settings, which is recorded in activity. While AI is off no run can be started and no request reaches a provider.
- **AI-002 — Run lifecycle.** A member can start a selection edit, a page or comment summary, a difference summary, a workspace answer or an agent run. A run moves Queued → Running → Needs review → Completed. A member can cancel a Queued or Running run; a run in any non-terminal state becomes Failed on error, and a run that has been Running for 10 minutes becomes Failed; Completed, Cancelled and Failed are terminal. A run reaches Completed when a member accepts or discards its output, and discarding records the rejection.
- **AI-003 — Review before mutation.** A run produces a proposal. Until a member accepts it, no page, block, record, comment, status, grant, container or state changes.
- **AI-004 — Answer provenance.** Every generated answer, summary and agent output names the pages and revisions it drew on, and each citation opens that revision (`REV-005`). A statement Slate cannot cite is labelled as unsourced and is never presented as drawn from the workspace.
- **AI-005 — Acceptance.** Accepting a proposal applies only the changes the member selected and records the accepting member, those changes, the input record revisions, and the provider and model. The mutations and their activity entries commit together or not at all.
- **AI-006 — Stale inputs.** A proposal whose input record has changed revision since the run started cannot be accepted until Slate shows the difference and the member either regenerates the run or accepts explicitly against the current revision.
- **AI-007 — Untrusted content.** Text inside pages, blocks, comments, imported documents, link cards and uploaded files is treated as content, never as instructions: it cannot change a run's scope, tools, destination records, agent permissions or the member's selections.
- **AI-008 — Provider and output failure.** A provider error, a budget refusal (`AI-012`), a cancellation, or output that fails schema validation leaves the run Failed or Cancelled with the reason readable on the run, creates no product record or partial proposal, and leaves every manual action available.
- **AI-009 — Retry.** Only transient transport failures are retried, at most 3 attempts over 5 minutes; invalid or unusable output is never retried automatically. A provider response received twice for one run produces one proposal.
- **AI-010 — Usage record.** Every run records the provider, model, tokens or provider units, calculated cost, duration, status, and the feature that started it. Totals are shown in settings by day and by feature.
- **AI-011 — No autonomous authority.** AI cannot publish or unpublish a page, change a grant or an external-access switch, set or renew a verification, archive, trash or permanently delete anything, empty the trash, invite or remove a member, change a policy value, or send data outside the deployment. Only a member action does these.
- **AI-012 — Budget.** Before a run is queued Slate reserves its maximum estimated cost — the configured model's rate for the run's maximum output — against the current calendar month, counting reservations held by unfinished runs. A run that would exceed the month's budget is refused before any provider request, and the unused part of a reservation is released when the run settles. AI cannot be turned on without a monthly budget greater than zero (`AI-001`), and no policy value, extension or agent raises, waives or defers the reservation.
- **AI-013 — A generated answer respects the reader.** Every answer, summary, chat reply and agent output is produced from pages the asking member may read at the moment it is produced, and says so on the result. A page whose grant is revoked, or which is archived, trashed or another member's draft, is excluded from the next answer with no re-index step, and no cache, embedding, transcript or stored proposal re-exposes it. This holds under every policy value and cannot be overridden.
- **AI-014 — Agents.** An agent is a named record with instructions, a source scope and an owner. It runs under the access of the member who invoked it, never wider (`AI-013`), its writes are proposals under `AI-003`, and its runs appear in the usage record with the agent named. 1.0 carries the agent object; model choice, per-agent budgets and per-destination write levels do not ship.
- **AI-015 — Workspace answers.** A chat panel answers questions over the workspace, keeps the conversation, cites its sources (`AI-004`), and places the same generated answer above search results with the same citations and the same caveat. It answers "I could not find that in what you can read" rather than answering from the model alone.
- **AI-016 — Summaries.** A member can summarise a page, its comment threads, or the difference between two revisions. A summary names the revision it read and is offered as text to insert or copy, never written into the page without `AI-003`.
- **AI-017 — Writing with AI.** With text selected, a member can run a named action — improve, shorten, lengthen, proofread, explain, translate, change tone — or a free-form instruction. The result is shown against the selection with insert below, replace, refine and discard, and nothing changes until one is chosen.

## Decisions

- **DEC-001 — A decision is a layer over a page.** A page can carry one decision record: a question, the options considered, an outcome, a rationale, an owner, a decided date, an optional review date, and citations to page revisions (`REV-005`). The page stays a page: its body, comments, grants, versions and links are unchanged, and removing the decision record leaves the page intact.
- **DEC-002 — States.** A decision is Proposed, Accepted or Superseded. Proposed becomes Accepted, an Accepted decision is superseded by another decision, and Superseded is terminal. Every transition records the actor and the time and appears in activity.
- **DEC-003 — An accepted decision is immutable.** Accepting freezes the decision's content together with the revisions it cites. Later edits to a cited page add a warning that the citation is no longer current and never rewrite the accepted rationale; changing an accepted decision is done by superseding it, which links both decisions in both directions.
- **DEC-004 — Decision log.** Decisions are listed and filtered by state, container, owner, decided date, review date and whether their citations are current, and each result opens the exact accepted revision.
- **DEC-005 — Review dates.** A review date that passes labels the decision overdue and notifies its owner (`NOTIF-001`) until a member records a completed review — a note, an actor, a time and the next review date — or supersedes the decision. An overdue decision stays Accepted.
- **DEC-006 — A proposed decision is editable.** While a decision is Proposed, a member with edit access on its page can change its question, options, outcome, rationale, owner, decided date, review date and citations. Each change writes a revision (`REV-001`) keeping the previous content with its actor and time, resubmitting an unchanged proposal writes no revision, and the history is readable under `REV-002`. Accepting the decision ends this, and `DEC-003` governs from then on.

## Out of scope

Declared in `seed.json` as non-goals and not tested: plan ladders, trials, credits, seat counting and in-product upgrade paths, because one product with every feature on has nothing to gate; a vendor product shelf, app switcher or cross-product chrome from a suite Slate is not part of; a commercial marketplace or storefront of templates, apps, agents or services, where Slate ships the extension surface and no shop; deep coupling to one issue tracker, where an issue URL is a link card like any other (`BLOCK-009`); a bundled screen recorder, where a recording is a media block (`BLOCK-008`); more than one workspace or tenant per deployment; real-time co-editing, shared cursors, presence beyond the avatar stack, and CRDT merge (`SAVE-006`); and a native mobile application, where the operator app is one responsive application that edits on touch (`NAV-006`).

Not in 1.0. Most of these arrive later as contributions on extension points this contract already names, and the seam is named with each: whiteboards and other contributed tree node types, and folders, on `tree.node-type.v1`; blogs, container calendars and container landing pages on `container.section.v1`; company hubs, team profiles, an activity feed, meeting notes and an assignments-across-containers view on `workspace.additional-page.v1`; a label filter over a container on `container.index.filters.after.v1`, over the label field core already stores (`PAGE-001`); and rollup and formula evaluation on `database.field-type.v1`, over the columns core already stores (`DB-002`). Three have no seam in 1.0 and arrive as a change to this contract rather than as a contribution, which is the honest statement about them: suggested edits, which is an editor mode; page locking, which is an authorisation behaviour beside `DB-009`; and per-agent budgets and model choice, which is enforcement inside `AI-012` and `AI-014`. Each is named in `research/editions/notion.md` or `research/editions/confluence.md` with the incumbent it belongs to.

## Retired clause IDs

These IDs were used by earlier versions of this contract in this repository and are never reused. No release is tagged, so none of them reached a buyer and none carries a `RELEASE.json` entry; this list is what keeps them out of every later release. The planning product's records — `BRIEF-001` to `BRIEF-008`, `DECI-001` to `DECI-005`, `PLAN-001` to `PLAN-009`, `MILE-001` to `MILE-004`, `TASK-001` to `TASK-008`, `RISK-001` to `RISK-004`, and `EVID-001` to `EVID-008` — are retired with the product-planning scope they described; the decision layer returns over pages as `DEC-001` to `DEC-006` and evidence returns as verification over revisions (`STAT-003`, `REV-005`). `WORK-002` (equal operators with no roles) is retired because this family has access levels, groups and guests. `WORK-004` (authenticated sharing only) is retired because this family publishes pages to the anonymous web under `WEB-001`. `WORK-005` (the planning overview) is retired in favour of `NAV-001`. `SAVE-007` (one long-text format) is retired in favour of `BLOCK-001` and `BLOCK-002`.
