# Product contract

## Target product and first buyer

Target approved 16 October 2026: **an owned Slack replacement for a US small or mid-size team**. Nook owns workplace messaging: channels, DMs and threads, dependable apps and push on every device, voice/video/screen sharing, an app and bot platform, shared external channels, and decisions and commitments that accountable agents follow through. A team using **Slack, Microsoft Teams or Google Chat** must be able to move its daily communication job here. **Campfire (37signals ONCE), Zulip and Mattermost** set an additional switching bar for owned operation, low-friction conversation and topic continuity. Their migrations are explicit obligations, not claims that each source exports every object.

The contract describes the product being built toward. `done`, `partial` and `missing` at the end describe current code/test coverage, not a reduced product or a shipped-service claim. US English, USD operating costs, US time zones/daylight saving and US business identity/privacy/records norms are the first-buyer defaults. Native clients, provider agreements, media transport, compliance custody and app approvals are engineering obligations, never exclusions.

Nook remains its own product. [Vector](https://github.com/just-every/run-vector) owns project work, [HappyToHelp](https://github.com/just-every/run-happytohelp) support, [Uptime](https://github.com/just-every/run-uptime) and [Lamp](https://github.com/just-every/run-lamp) their alerts/operational signals, and [Chest](https://github.com/just-every/run-chest) file ownership. Nook connects their records to conversations; no sibling, market gap or office-suite product is absorbed. Sharing implementation is welcome; merging the products is not.

## Product target

| | |
| --- | --- |
| Category | Team chat and workplace messaging |
| Incumbents a buyer switches from | Slack, Microsoft Teams, Google Chat, Campfire (37signals ONCE), Zulip, Mattermost |
| First buyer | A US small or mid-size team that runs its daily communication on Slack, Microsoft Teams or Google Chat and wants an owned replacement |

## Published core clauses

Each clause is `- **ID — Title.**` followed by its text. NOOK-001 to NOOK-015 are the clauses earlier documents call C1 to C15 (NOOK-014 is C14, for example); a reference to Cn means NOOK-0nn.

- **NOOK-001 — Current identity and conversation membership (C1).** Every private API validates current active identity and current conversation membership. Search/replay/outcomes use the same policy. Workspace admins cannot read arbitrary DMs.
  **Acceptance evidence:** Through signed-in HTTP, SSR, search, replay and generated loaders, revoke or expire a session and remove a participant; every subsequent private read/write is refused, including a workspace admin attempting another person’s DM.

- **NOOK-002 — Atomic messages, retries and replay (C2).** Messages commit atomically with per-conversation event sequence and client intent key. Identical retries return the same message; changed retry payload conflicts. Replay pages preserve ordering; edits use compare-and-swap versions; deletes tombstone and erase original body from live revisions.
  **Acceptance evidence:** Send through the conversation API, repeat the same intent after a lost acknowledgement, then change its payload; observe one ordered message, the original retry result and a conflict for changed intent. Race edits and delete; replay shows current tombstones without deleted bodies.

- **NOOK-003 — Fixed DMs and channel membership (C3).** DMs have fixed participants. Channel owners control membership. Removal revokes reads and writes immediately, including replay and search. New members currently see entire channel history; UI and docs must disclose this.
  **Acceptance evidence:** Create channel/private/DM conversations through the app, add a channel member after earlier messages, and observe full-history disclosure and access. Remove that member and deny history/search/replay/files immediately; refuse DM participant changes and unrelated-admin DM access.

- **NOOK-004 — Decisions and commitments (C4).** A decision has a human author, conversation scope and immutable version history. Only its author revises it with version comparison. A commitment can become accepted only through the assignee's action. Completion is reported, never labelled verified.
  **Acceptance evidence:** From a discussion, record a decision and propose a commitment. Another participant cannot revise the decision or accept for the assignee; the assignee accepts the exact version and reports completion. Reopen history and see human authorship, source context and reported status.

- **NOOK-005 — Read cursors and attention (C5).** Read cursors persist per user and conversation, are monotonic and cannot exceed durable frontier. Attention/unread is separate from accepted responsibility.
  **Acceptance evidence:** Read a thread from two sessions, reconnect one, and observe monotonic unread recovery with own sends excluded. Refuse read markers beyond the durable frontier; closing Attention does not accept work or mark unseen messages read.

- **NOOK-006 — Offline restore (C6).** Restore is offline into an empty database with version and checksum verification and SQLite integrity checking. Current backups contain private content, never live sessions; password hashes belong only to explicitly supported legacy recovery inputs and never become new sign-in authority. No external effects on restore.
  **Acceptance evidence:** Use rer export/reset/import into an empty stopped installation, complete explicit identity recovery, and compare conversations, memberships, outcome versions, attention and downloaded file bytes. A populated destination, altered checksum or invalid graph is refused; no old delivery or session revives.

- **NOOK-007 — Safe rendering and local binding (C7).** Human text renders inertly. No message, plugin, link or AI output can trigger arbitrary code, client-side or credentialed fetching, or provider effects; the only server-side link fetch is NOOK-068's bounded, SSRF-safe preview service. Authorized app/agent/meeting effects require the explicit scoped doors in NOOK-022–030 and NOOK-054–059, not ambient conversation authority. Bounded bodies, session expiry, origin validation for mutations and loopback-only development binding remain required; production requires HTTPS. New sign-in uses invited Base magic links; legacy password hashes remain offline recovery inputs only.
  **Acceptance evidence:** Post active markup and malformed/oversized bodies through public APIs; markup stays inert and invalid input fails without writes. Cross-origin mutations, foreign sessions and unsafe local origins fail; production output excludes dev admission.

- **NOOK-008 — Evidence limits (C8).** The complete target in this contract remains an obligation until its acceptance evidence exists. Local tests, synthetic imports and substituted network edges establish only their bounded paths; hosted scale, legal/compliance operation, real inbox/device receipt, native push and live provider integrations require separate evidence. Missing proof changes the status, never the scope. Delivery/provider acceptance, device display, human reading and action are distinct facts.
  **Acceptance evidence:** For a release candidate, reproduce the documented local public-door journeys, then separately record hosted multi-device, provider, migration and capacity results before those target clauses become done. Failed or unmeasured end states remain visible.

- **NOOK-009 — Backup and export (C9).** Two full backup paths ship, neither an ordinary app-admin export. On a local install, the filesystem operator uses local-only `rer export` and `rer import` on a stopped checkout; operator disk access explicitly bypasses app privacy. For a hosted install, the [hosted backup/export path](docs/backups.md#export-a-hosted-nook) uses the owner’s `/export` download or the documented `node --input-type=module` command calling `scripts/local/remote-export.mjs` from a checkout with dependencies installed; it exports the authoritative D1 database records and actual private R2 object bytes into an owner-held verified archive with its erasure journal. Nook's `/export` and `/api/portability` doors admit only an active owner's current session and authorize no import. [Base's hosted-data rule](https://github.com/runeditrun/base/blob/main/export/RUN-LOCALLY.md#if-hosted-deployment-stops) distinguishes this from local-only `rer export`, which refuses a hosted origin: a platform D1 SQL export alone omits R2 files and cannot satisfy this full-backup guarantee. Restore is always local, into an empty destination, and validates table identities, foreign references, schema ledger and event/read frontiers. Archives exclude sessions and sign-in tokens. User-readable and policy-scoped exits are required by NOOK-047; this clause retains the full-backup guarantee and its explicit operator-custody exception.
  **Acceptance evidence:** **Local:** Stop the install, export with `rer export`, import into an empty stopped local install, complete explicit identity recovery and re-export; compare equivalent records, private media bytes and erasure journal. **Hosted:** Sign in as an active owner, follow the hosted backup/export path through `/export` and the documented helper command to download D1 records plus R2 bytes into an owner-held archive, import it into an empty stopped local install and complete identity recovery; compare records, file bytes, rebuilt search and erasure journal. Reject non-owner/expired hosted sessions and any remote restore. Inspect both archives for private records and media, with live credentials, devices and pending deliveries excluded.

- **NOOK-010 — Commitment revision and cancellation (C10).** Material text/deadline revision requires renewed assignee acceptance at the exact new version. Assignee and conversation are fixed; changing assignee means explicit cancellation and a fresh proposal, and scope cannot move across conversations. Cancellation is explicit, terminal and versioned; authors may cancel proposals/accepted work but cannot claim assignee completion. Deleted source refs become null; event rows preserve opaque sequence/object facts, never message bodies. Replay clients fetch current object state under current ACL and interpret tombstones.
  **Acceptance evidence:** Accept a proposal and revise its text/deadline: renewed acceptance is required at the new version. Cancel and create a new proposal to change the fixed assignee. Refuse stale acceptance, cross-conversation moves and author impersonation of completion; cancellation stays terminal in history and deleted source refs reveal no body.

- **NOOK-011 — Cached retries, synchronization and complete history (C11).** Cached retry responses require current ACL before lookup. Reads synchronize monotonically across sessions; replay cursors indicate per-conversation sequence and bounded catch-up, not read receipts. History pages must cover the complete tested log rather than silently truncate.
  **Acceptance evidence:** Page beyond the tested history window and reconnect after missed edit/delete events. Repeat a previously successful intent after membership revocation; it must be denied before cached response lookup, with no missing or duplicated durable history.

- **NOOK-012 — Private attachments (C12).** Attachments are 1 byte–25 MiB per file, five ordered files per message, with at most 50 pending/staged native uploads per uploader and no aggregate workspace byte quota. Actual bytes reside in private R2; D1 holds metadata and opaque object keys. Plain UTF-8 text, signature-checked PNG/JPEG/PDF only; forced attachment downloads plus private inline PNG/JPEG previews after structural byte validation and opt-in plain-text previews bounded to 64 KiB; no inline PDF preview or virus-scanning claim. Staged uploads last24h for binding/read eligibility, are uploader-only under current conversation ACL, and do not create attention. Unused staged bytes remain until explicitly removed. Binding uploader-owned same-conversation IDs and message/intent/event is atomic; exact ID order belongs to retry identity. Downloads recheck current membership; delete erases live bytes/name/checksum, and message deletion cascades. The media baseline archivev2 carries actual bytes plus hashes, state and binding; historical archivev3 retains these guarantees plus C13 triage and supports explicit prior-v1/v2 migration; current normalized Base portability archives retain the same custody guarantees, as described below. Backups/WAL/remnants retain their stated physical-erasure limits. These limits are the shipped baseline, not the target: NOOK-061 widens admission to any file type up to 1 GiB with in-app previews, malware scanning and admin file-type policy while keeping every custody guarantee in this clause.

  **Acceptance evidence:** Upload and bind ordered private files through the app, preview genuine PNG/JPEG and download exact bytes as an authorized participant. Deny outsiders and removed members, reject active/corrupt/oversized input, recover lost-put acknowledgements and verify deletion and backup round-trip.

- **NOOK-013 — Calm attention (C13).** Calm attention is explicit per-user triage, independent of unread and outcome authority. GET/PATCH `/api/conversations/:id/preferences` accepts boolean `muted`/`followed`, requires current membership, and returns those choices plus followed root-thread IDs. POST `/api/messages/:id/follow {follow:boolean}` targets a live root thread under current ACL. Choices persist across sessions; changes affect future routing only. Muting suppresses routine follows, never explicit mentions or commitment proposals. Automatic following when a member authors, replies in or is mentioned in a thread, and of every thread in a one-to-one DM, is NOOK-072 and writes this same followed state; an unfollow through this door also ends an automatic follow.

  Message sends accept ordered unique `mentions` (at most50 current member IDs), boolean `urgent`, and `urgentReason` (1–240 characters when urgent). Urgent requests require explicit recipients. All current channel members can see the message's Notified names, Urgent marker and urgent reason, including members who were not notified. Defaults `[]`, `false`, `null` join immutable intent identity. Identical committed retries survive recipient removal without rerouting; new sends revalidate recipients. Attention creation is atomic with durable message/outcome events and stores opaque references, not copied bodies. Precedence: urgent-request, mention, thread-follow, conversation-follow. Proposals and material revisions route to assignees; assignee actions route updates to authors. Self-authored events do not create self-attention.

  GET `/api/attention` returns current-ACL hydrated active items, `deferredCount`, `dndUntil`. Deleted sources hydrate tombstones, with no urgent-reason copy. GET/PATCH `/api/settings {dndUntil:epochMilliseconds|null}` controls quiet time. Quiet time defers routine items while urgent in-app requests remain visible; external delivery follows C14 email and C15 Web Push, and there is no paging guarantee. POST `/api/attention/:id {action:done|snooze,until?:epochMilliseconds}` requires item ownership and current ACL. Quiet/snooze deadlines must be future times within seven days. Snoozed items become eligible when due; routine items remain deferred during quiet time. Done changes only triage. Reading or accepting a commitment does not automatically close attention, and closing attention never accepts work.

  Schema5 preserves prior ledgers and appends explicit defaults to live prior intent payloads; erased markers remain erased. Archivev3 retains follows/preferences/quiet time/triage and validates source/event/scope references. Explicit v1/v2 imports add empty attention tables and normalize prior retry identity. Old-format archives cannot carry nonempty new attention state. Root SQLite v3 and native D1 recovery formats require an explicit separately verified mapping; a source manifest alone does not establish compatibility.

  C13 admission clarification: defaults apply only to omitted mentions/urgent fields, not explicit null. Explicit mentions:null or urgent:null is400 with no committed event/message/attention. Recovered dnd_until must be null or a safe integer; expired valid integers remain legal recovery values. Quiet-time domain checks precede database insertion.

  **Acceptance evidence:** At 1440 and 390, follow a thread, mute its channel, send explicit mentions/urgent requests, snooze and complete triage, and enter quiet time. Restart and sign in elsewhere: preferences persist, due items return under current ACL, and neither triage nor reads accepts responsibility. Automatic follows from authoring, replying, mentions and one-to-one DMs are proved under NOOK-072; an unfollow through `POST /api/messages/:id/follow {follow:false}` ends one.

- **NOOK-014 — Email notifications (C14).** Email notifications reach a member away from the browser under this delivery contract. Emailable events are an explicit mention, an urgent request, a commitment proposal or material revision to its assignee, and a completion report to its author; routine thread or conversation follows, reactions, replies without a mention, accept/cancel updates and imported history are never emailed. The destination is the member's own sign-in email (an active account, verified by its sign-in link); there is no free-form address. Every member's email is off until that member turns it on in their own settings, per account: one choice covers mentions and commitments, a second covers urgent requests, with a separate choice to let urgent requests through quiet time. GET/PATCH `/api/notify {mentions,urgent,urgentQuiet:boolean}` reads and changes the caller's choices. Turning a choice off stops the next email: each queued item is checked again when its email is built. Every email carries a link that turns email off without a session (`/notify/off`: opening it shows one button and changes nothing, because mail scanners open links; the button turns all three choices off) and a link to the signed-in settings.

  Queue rows are written in the same batch as the attention rows, only for recipients who turned the matching email on, and hold opaque references (recipient, conversation, actor, message or commitment, reason), never message text, file names or addresses. The ChatHub alarm drains them; pending rows keep it armed, so delivery needs no open socket and a restarted Worker resumes from D1. When an email is built, nothing is sent to an inactive member, about a conversation the member can no longer read, about a deleted or erased source, after the choice was turned off, or for an item the member already marked done or a mention they already read; each is cancelled with that reason. Quiet time (C13 `dndUntil`) holds every item until it ends; an urgent request goes through quiet time only when its recipient chose that. Each event is queued once per recipient (keyed by its attention item), so identical retries add nothing; a burst within five seconds folds into one email, and a recipient gets at most one email per conversation per ten minutes, with later items folded into a count. A failed send is retried after 15 s, 1 min, 5 min, 30 min and 2 h, then given up; a definite refusal is not retried, and a send the provider did not confirm, or that a stopped Worker may have made, is never repeated. A send being retried or given up shows as "email not delivered" with its reason class (not set up, refused, rate limited, not confirmed) to its recipient, to the member whose message caused it and to the owner for seven days, never as a silent success. Emails name the sender, the conversation (`#name`, or "a direct conversation") and a link; they never carry message text, file names, urgent reasons or commitment wording. This no-text form is today's behaviour and the `sender` level an owner policy can force; a member's opt-in to message text in email is NOOK-062's obligation.

  Hosted mail goes through the same configured sender as sign-in (the owner deployment's provider). Local development sends and stores no email and exposes no mail viewer. Tests fake only the provider network edge. Accepted by the mail provider is not proof of inbox receipt, and a test provider's acceptance is not live delivery. Urgent stays an in-app request, not paging; Web Push follows C15; there is no SMS or native push. Archives carry members' choices but not the queue, so a restore sends nothing old; erasure removes a member's choices and queued items and an erased message's items.

  **Acceptance evidence:** Run the emitted-Worker notification journey through sign-in and settings with only the provider network edge substituted: opt in, mention/propose, fold bursts, hold quiet items, cancel read/deleted/revoked items, restart a retry, expose refusal/uncertainty and use the scanner-safe unsubscribe POST. Inspect that mail contains no private body or file name.

## Native consolidation

The earlier root native Worker used schema4 and archivev4; current app authority is normalized D1 and private R2 as described below. Applied D1 migrations0001–0003 are unchanged;0004 adds immutable external history with no historical events, unread, attention or work acceptance. Historical root ledger1–6/archivev1–v4 and native archivev2–v3 are explicit recovery inputs; imported receipts/provenance/media remain independently recoverable. The retired Node/SQLite runtime and composition outputs are no longer application entry points. C1–C13 remain obligations, including the historical transitions described above. Historical document archives retain their original admission bounds. Normalized authority removes the whole-document size ceiling and never prunes history silently.

## Sign-in and identity

C1, C3, C9 and C11 remain current-identity/current-membership obligations. Every
private API, SSR document and workspace loader resolves a verified Base operator-auth
session and stable product ID. Owner/admin roles do not bypass conversation ACLs.
OA session expiry/revocation/operator-version fences are repeated in D1 mutation
CAS and guarded reads because auth-table changes do not advance workspace
revision. Reads do not initialize or modify the product document.

C6/C7's legacy password wording describes old recovery inputs only. New accounts
use explicit owner bootstrap and invited magic links; open registration/password
login are removed. Confirmation GET/HEAD never consumes a link; native POST
consumes once. Native and cross-device sign-in (finding the installation, managed deployment, email codes, universal and app links, sign in on mobile from desktop, personal providers and system-browser SSO) is NOOK-074. Local development uses per-checkout-key-gated dev sessions:
owner entry through `RER_OPEN` and `/setup` precedes fixed local persona entry in
the admitted browser. Personas do not accept arbitrary email invitations.
Local development sends and stores no mail and exposes no mail viewer.
Configured production auth requires HTTPS, a supported mail provider and trusted
platform client IP; it retains default Base rate limits. Default Base roles are
owner/admin/member.

Normal archives use Base portability with semantic product validation, integrity
checking, empty-target restore and explicit credential exclusions. Legacy archive
v1-v4 conversion remains a separate recovery boundary. Prior password users have
no inferred mailbox: an explicit complete stable-ID/mailbox/role mapping and
active owner selection atomically establish auth authority in a new offline copy.
Multiple owner roles are preserved; recovery selects one explicitly active owner.
Inactive IDs, all private references and original recovery bytes are preserved.
No fresh owner automatically receives prior conversations.

## Native normalized storage (Part 3)

C1–C11 and C13 remain obligations. App reads and mutations use indexed row-level D1 storage after an explicit offline authority cutover. Every mutation batch fences the current Base operator ID, version, active status, unrevoked unexpired session and current conversation membership inside SQL. Events allocate one contiguous sequence in the same batch as the message/revision/outcome/attention/attachment changes. Internal event actors are retained where known; public replay remains opaque. Membership confers historical access, including fixed DM membership; a workspace admin receives no extra DM access.

C12 admits 1 byte–25 MiB per file, up to five ordered attachments per message, and at most 50 pending/staged native uploads per uploader. There is no product-wide aggregate media-byte quota; actual D1/R2/platform/account limits still apply. Bytes live only in the private R2 binding, never a base64 D1 cell. Binary requests are bounded by consumed bytes; legacy JSON file requests remain explicitly bounded. Filename, declared MIME, signatures and UTF-8 checks remain required, and these checks are not virus scanning (NOOK-061 adds scanning). Native upload intent reserves an opaque object key durably before put, then finalizes with fresh authority. Retries after lost acknowledgements use the reservation; only finalized staged uploads bind. Staging expires after 24 hours for binding and download, and unused files require explicit removal. Deletes tombstone metadata and persist the physical-delete obligation before R2 erase. Recovery retries the erase; it never compensates an uncertain finalize by deleting a potentially bound object. Download responses force attachment disposition with no-store/nosniff. A separate PNG/JPEG preview endpoint validates the image container, rechecks current membership after reading private bytes and returns inline image bytes with private/no-store, nosniff and a sandbox CSP. The client renders a revocable blob URL in an img element. Plain-text files also have an opt-in private preview bounded to 64 KiB, rendered as inert text with a visible shortened-preview label when truncated; exact original bytes remain downloadable. PDF files remain downloads; there are no public object URLs.

History and replay seek indexed `(conversation,sequence)` frontiers. Author-aware unread counts native messages after the persisted read frontier, excluding the reader's own messages and all non-message events. Sending does not advance the read frontier.

Web Drafts & sent lists this browser's account- and installation-isolated drafts in currently accessible conversations, including thread context and unconfirmed delivery state. Its Scheduled section reuses schedule editing and cancellation; Sent pages the caller's messages from the last 30 days, newest first, with current ACL and current revisions. Draft enumeration pages current conversation access; the workspace conversation list also enumerates all currently authorized conversations without a 1,000-entry cap. Web composition provides Markdown source with accessible rich preview, typed-format conversion and undo, person/channel mentions, emoji, text export, exact-byte previewable snippets and resumable split sends. Built-in slash commands use current-authority APIs and private confirmations. Profiles, chosen time zones, expiring statuses, optional typing/presence, field privacy, a fully paged 5,001-person directory, private sidebar sections, explicit mark unread and separate-origin installation links with independent sign-in ship. NOOK-017 still requires native-client draft/status/workspace-switch journeys, calling/GIF service effects for `/huddle` and `/gif`, app-command ordering/name reservation and Slack-compatible length errors under NOOK-021/022/025/063/070; no native workspace picker is claimed.

Search combines Unicode terms of every length, exact phrases, token prefixes (`*`), case-insensitive substrings and exclusions (`-`). Typed modifiers appear as removable chips and cover channel/person, participant, conversation type, time-zone date ranges, links, reactions, threads, saved items, pins and files. A positive non-prefix ASCII term of at least three characters uses one materialized FTS5 trigram candidate set per request; other queries scan eligible history under the same filters and current ACL. Current native and published imported file names and extracted plain-text/text-bearing PDF contents are searched from private R2 bytes, with live source and membership checks before results return. Unsupported or unindexed contents are named explicitly; extraction is bounded to 25 MiB of input, 100 PDF pages and 1 MiB of text. The emitted Worker journey pages an authenticated 80,000-message corpus completely without duplicates or omissions. Search pages examine at most 2,000 eligible candidates and return at most 100 authorized results with an opaque continuation; a sparse page is not a declaration that no later matches exist. Native clients and wider file admission remain NOOK-021 and NOOK-061 obligations.

Workspace and conversation Files views page native and published imported files newest first, filter by type/person and open authorized download, image preview and source context; their directory-based filter pickers share the current 1,000-entry directory limit. File-content search is in Search, rather than these Files views. Attention is paged at 100 items with an explicit continuation and page-scoped deferred count. The workspace actor snapshot and the Files filter pickers still cap at 1,000; the workspace conversation list has no such cap, and the separately paged people directory and Drafts & sent access enumeration are not subject to the actor-snapshot cap. The remaining directory limits do not imply an unlimited workspace claim. NOOK-049 requires every directory and the channel browser to page and search completely at its stated capacity.

`/api/live` carries only authorized conversation/frontier hints (and an empty-workspace refresh control), never event bodies, actor IDs or recipients. D1 replay is authoritative. Durable Object frontier comparison repairs a lost final post-commit hint, and reconnect always catches up through replay. Current session and ACL checks close revoked subscribers. The live channel delivers nothing outside the browser; C14 email is drained by the same Durable Object's alarm.

Schema migration 0010 appends normalized tables without changing 0001–0003. Existing documents stay explicitly authoritative and application traffic is paused until the offline converter validates all references, counts and R2 bytes, atomically marks normalized authority and removes the legacy document. Historical archive conversion is explicit; native legacy archive formats remain recovery inputs. Normalized backups use actual Base portability over D1 plus R2, retain stable product actors, exclude credentials/sessions/live effects, reject pending object work, and rebuild derived search after restore. Restore requires explicit identity mapping before bootstrap.

- **DOCS-001 — Public documentation from Markdown.** Public user documentation is rendered from Markdown sources declared in `docs/site.json`. Every root and `docs/` Markdown file is published or has a reason for remaining GitHub-only; missing files, links and heading anchors fail the build. Raw HTML in a page is reported and never rendered.

  **Acceptance evidence:** Build and open /docs; every declared page renders, links and anchors resolve, raw HTML remains inert, and every root/docs Markdown source is published or has a recorded exclusion reason. The app Lobby and private workspace keep their existing routes.

- **ERASURE-001 — Member, message and imported-author erasure.** Current owners/admins may erase accessible channel messages; roles never open an operator DM door. Authors retain their own delete/remove actions. Owners may erase eligible other members; admins only accounts with the member role, including guest access; self and the last active owner are refused. Every destructive commit guards current session, role and scope. Count-only preview, explicit confirmation and a subject-free receipt accompany operator erasure. Authored content, credentials and selected import copies are erased; authored work retains only scope/kind/version count/state. Other authors retain work and state without erased assignee/action attribution. Separate credential-free structural tombstones preserve threads, counters, fixed DM audiences and immutable import ownership; audit attribution and subject references are independently anonymised. D1 atomically records public Base erasure fences and durable blob obligations before R2 deletion. Completion requires verified blob absence, including resolution of uncertain puts; larger work remains pending with bounded continuation. Latest archives carry the journal and restore normally. Old archives are refused by nonempty-destination guards or the latest journal; a virgin destination without a later journal cannot recognize them. External archives, offline copies, provider backups/Time Travel and physical remnants remain outside logical erasure.

  **Acceptance evidence:** Through Team and message erasure doors, preview counts, confirm and resume a pending job. Try an unrelated DM, an insufficient role, self and last owner; deny each. Remove native/imported content and verified R2 bytes, preserve only declared structural tombstones, anonymise audit, and refuse an older archive when the latest erasure journal is supplied.

- **NOOK-015 — Web Push (C15).** Web Push is a separate opt-in channel under C14's current-identity, current-ACL, source, read/done, quiet-time, retry, visible-failure and no-paging rules. Migration 0023 preserves existing email rows and choices. **Defaults for a new member:** once the member grants browser notification permission, and without opening settings, DMs, mentions (including NOOK-065 broadcast mentions), keyword hits (NOOK-032), call invites (NOOK-022) and replies in followed threads (including NOOK-072 automatic follows) are on, matching Slack's default of desktop and mobile notifications for DMs, mentions and keywords; urgent-through-quiet starts off. Each member may change any of them, and owners set the organization defaults under NOOK-060. GET/PATCH `/api/notify` adds `push:{mentions,urgent,urgentQuiet,direct,threads,keywords,calls:boolean}`. Mentions covers the same commitment events as email. Ordinary DMs and followed-thread replies can push without changing email or in-app Attention. Conversation-follow for Attention alone never pushes; only a conversation set to the NOOK-032 `all new messages` level pushes each new root message and channel-broadcast thread reply. Explicit urgent and mention reasons take precedence and require their own choice; a routine followed reply precedes ordinary DM when both are enabled. Routine follows and DMs respect mute; self-authored and imported events never push.

  POST `/api/devices {transport:"webpush",subscription:{endpoint,keys:{p256dh,auth},expirationTime?:number|null}}` registers only the caller's browser subscription under the current session. GET returns only the instance VAPID public key and the caller's opaque device IDs, transport and timestamps. DELETE `/api/devices {id}` removes only an owned device. Endpoint ownership cannot transfer between accounts: unsubscribe in the browser before enabling another account. At most ten devices per person; HTTPS endpoints are restricted to the supported Apple, Google and Mozilla push hosts, keys must be valid P-256/16-byte material, redirects are refused. This browser door accepts only `transport:"webpush"` and rejects every other transport; native iOS and Android devices register their APNs, PushKit or FCM tokens through the native device door and push path that NOOK-078 defines, and desktop apps receive over their own authenticated connection to the installation. Web Push needs no publisher service: each installation signs with its own VAPID key and sends straight to the browser vendor's push service. Each delivery rechecks the device's original session, active version, membership, preferences, native source and unread/triage state. Expired or revoked sessions and provider-expired subscriptions retire the device.

  One durable queue row per event, recipient and device prevents duplicate alerts from overlapping reasons and successful-device repeats during another device's retry. Email and push have independent five-second gathering and ten-minute conversation folding windows; push windows are per device. The first due eligible item gathers the already-queued eligible burst, including items still in their initial gathering period; future retries, snoozes and quiet-held items stay deferred. A counted push uses one opaque delivery ID for the whole burst, and remains current while any folded item remains current. A conditional sending lease becomes visibly not-confirmed if interrupted, and uncertain network sends are never repeated. Failure classes show to the recipient, the sender and the owner for seven days; terminal delivery metadata lasts thirty days. Read and done transactions cancel pending push. Snoozes and quiet time defer delivery; only the separately enabled urgent-through-quiet choice overrides quiet time.

  POST `/api/devices/presence {id,page,visible:boolean}` records a current-session visible-page heartbeat. Today fresh visible presence holds all of the member's Web Push; NOOK-073's cross-device active-elsewhere rule replaces that hold for Web Push exactly as for native push: a focused page with recent input is an active desktop client, Web Push to the member's phones and tablets is held while they are active on desktop and released or cancelled under NOOK-073, and the active client itself shows the alert in-app. Open sockets alone never count as presence. Foreground pages renew every fifteen seconds, hidden pages release, and the alarm automatically releases expired forty-five-second presence. No browser page is needed for delivery. GET `/api/devices/sync` supplies current opaque delivered-notification IDs and badge count for foreground synchronization; the count includes unread active attention and ordinary push-enabled DM events, deduplicated across devices.

  Web Push uses RFC 8291 encrypted payloads and RFC 8292 VAPID. The instance generates its key pair once on first authenticated use, storing the private key AES-GCM encrypted under a key derived from SESSION_SECRET, and sends only its public key to browsers. The encrypted payload contains sender/conversation display wording (DMs use "a direct conversation"), an authenticated same-origin app link with opaque conversation/message IDs, opaque recipient/delivery IDs, notification tag and badge count. No message body, commitment text, urgent reason or file name is included; this is the `sender` preview level, and NOOK-062 adds default-on message-text previews (inside the same encrypted payload), org caps and notification actions. Push services see the endpoint, timing, encrypted packet size, TTL/normal urgency, VAPID public key and signed audience/expiry/contact URI. Subscription endpoints, public encryption keys and auth material remain private D1 records. Archives exclude keys, devices, presence and all delivery state; restored installations send nothing old. Member/message/outcome erasure removes associated push records. Logical erasure cannot retract packets already accepted by a push service or notices already displayed on a sleeping device.

  The installable PWA preserves Nook's UI and authenticates navigation on every notification tap. Its service worker caches no requests or private content. Permission requests require a gesture; the foreground reconciles allowed subscriptions and visibly reports re-enable failures. The previous open-tab OS notification path is removed; the title count remains. Every Web Push shows a visible notification and can update its badge. No badge-only or clear-only background Web Push is sent: WebKit revokes silent subscriptions. Foreground authoritative refresh closes read/revoked notices and updates badges through the service worker. Immediate clearing on a sleeping device, native iOS badge updates and Android native normal-priority clears remain blocked until a native transport exists. Browser acceptance is not proof of OS receipt; C8's evidence limits still apply.
  **Acceptance evidence:** Run pnpm test:push through subscription, preferences, emitted Worker and real alarm; independently decrypt the RFC 8291 packet and verify VAPID, revocation, quiet/read cancellation, per-device folding and visible failure. Separately demonstrate foreground/background/locked-device visible notification, tap authentication and badge reconciliation on supported Safari, Chromium and Firefox devices. **Defaults:** a newly invited member grants browser permission in a fresh profile without opening settings, and a DM, a mention and a keyword hit each show exactly one notification; a channel set to `all new messages` notifies on a colleague's plain message and a channel left at `mentions` does not. **Defaults include automatic follows:** a reply in a thread the member started in a `mentions` channel shows one notification without any explicit follow. **Cross-device routing:** with the member's Chrome page focused and in use, a DM shows on that page and no Web Push reaches their phone's installed PWA; after the page is idle 10 minutes the phone notifies (NOOK-073).


## Target clauses

### Complete workplace messaging target

- **NOOK-016 — Everyday conversations.** People create discoverable team channels, private channels, one-to-one and fixed-participant group DMs; send messages, open replies in a root thread, add/remove emoji reactions and edit/delete their own messages. A durable link opens the intended conversation/thread under current membership. Channel browsing and idempotent join never reveal private conversations; guests cannot browse or join outside their grant. Members leave any channel they belong to themselves, from every client and the API, except an organization-wide default channel an owner has made mandatory. Leaving removes the channel from navigation, unread and notifications and keeps the member's messages; returning to a private channel needs a manager to add them again, and the last manager of a private channel must transfer management before leaving.

  **Acceptance evidence:** Use two signed-in people at 1440 and 390 to create/join a channel, exchange roots/replies/reactions, open a thread link after reload and edit/delete a message. An outsider and a guest fail to discover private content or expand access. A member leaves a public and a private channel: each disappears from navigation, unread and notifications; they rejoin the public one, are refused rejoining the private one without a manager, and are refused leaving the mandatory default channel.

- **NOOK-017 — Composition and personal workspace.** The composer supports accessible rich text, lists, code blocks, safe links, emoji, explicit person/channel mentions and ordered attachments, with consistent plain-text export. **Length and typed markup:** a message holds up to 40,000 characters, Slack's limit. The composer shows the remaining count from 36,000, and pasting or typing beyond the limit offers to post the text as a snippet (a NOOK-061 text attachment with an inline preview) or to split it, never truncating silently. Every write door (send, edit, API, and NOOK-063's `chat.postMessage`, which answers Slack's `msg_too_long`) refuses over-length text with `message_too_long` and writes nothing. As typed, `*bold*`, `_italic_`, `~strike~`, `` `code` ``, triple-backtick blocks, `>` quotes and `- `/`1. ` list starts convert to formatting; one undo restores the literal characters, and each member can turn the conversion off. Private drafts survive navigation and account-isolated restart; editing, pending send and failed send are distinct states. People set profile, time zone, presence and a custom status (emoji and text) with Clear after: never, 30 minutes, 1 hour, 4 hours, today, this week or a chosen date and time in their time zone, clearing at that moment on every client, organize channel/DM sections, mark unread, and switch workspaces without mixing drafts, caches or notifications. **Drafts & sent:** one view lists the member's unsent drafts across conversations, their NOOK-034 scheduled messages and the messages they sent in the last 30 days, each opening in its conversation; editing or cancelling a scheduled message there changes it under NOOK-034. **Built-in commands:** typing `/` lists built-in commands, with arguments and a description, before NOOK-025 app commands, and no app may register a built-in name. Each runs through the same API, permission check, audit and confirmation as its menu action: `/remind` (NOOK-033), `/invite @person [#channel]` (NOOK-016 and NOOK-018, under channel-manager and NOOK-060 policy), `/leave` and `/who` (NOOK-016), `/topic` and `/rename` (NOOK-060), `/mute` (NOOK-032), `/dnd [duration or time]` (pause notifications until then, NOOK-032), `/status [emoji] [text]`, `/away` and `/active` (this clause), `/msg` and `/dm @person [text]` (open or send a DM), `/huddle` (NOOK-022), `/gif [words]` (NOOK-070), `/shrug [text]` (appends `¯\_(ツ)_/¯`), and `/collapse` and `/expand` (hide or show every inline preview in the conversation for that member). A command the member lacks permission for answers privately with the reason and changes nothing; an unknown command posts nothing and offers to send the text as a message. The NOOK-052 shortcut sheet lists the built-in commands. Typing/presence are advisory and may be disabled. Admins define custom profile fields (title, department, phone, manager, location, pronouns, start date) with per-field visibility; a searchable people directory filters by name and those fields, pages completely at NOOK-049 capacity, honours guest scope and never exposes a hidden field.

  **Acceptance evidence:** Compose formatted text/code and an attachment, leave and resume a draft on each supported client, deliberately interrupt a send and reconcile it once. Switch accounts/workspaces and inspect draft isolation; mark unread and change status/time zone without changing work acceptance. Set a status to clear after 30 minutes and see it disappear on another client at that time, across a restart. Define title, phone and manager fields, fill them and find a person by department in the people directory at 1440 and 390; a guest sees only people in their granted channels and no hidden field. Paste 50,000 characters: the composer offers a snippet or a split, and posting as a snippet yields one message with a previewable text attachment; an API post of 40,001 characters is refused with `message_too_long`, and one of 40,000 posts whole. Type `*bold*`, `_italic_`, `~strike~`, `` `code` ``, a fenced block, `> quote` and `- item`: each converts as typed, one undo restores the literal text, and the plain-text export keeps the content. Type `/` and see the built-in commands before app commands. Run `/remind me tomorrow at 3pm to send the deck`, `/invite @ana #sales`, `/topic Q4 launch`, `/rename #sales-us`, `/mute`, `/dnd 1 hour`, `/status :palm_tree: Out until Monday`, `/msg @ana hi`, `/huddle`, `/gif thumbs up`, `/shrug ok`, `/collapse` and `/leave`: each has the same effect, permission check and audit as its menu action. `/rename` by a member without rename permission is refused privately with the reason, and `/wat` posts nothing and offers to send it as text. Open Drafts & sent and find an unsent draft from another channel, a scheduled message (change its time there) and yesterday's sent message.

- **NOOK-018 — Invitations, roles, guests and offboarding.** An owner bootstraps the installation and owner/admin/member roles govern team administration. People join through exactly four doors, each ending in a confirmed mailbox: a one-use email invitation, an owner-controlled invitation link, approved-domain self-join, or SSO just-in-time sign-in (NOOK-037); an address that arrives through none of them cannot register. Every invitation and join-confirmation email carries a one-time code, an open-in-app link and a QR code for the native apps (NOOK-074). NOOK-075 external participants are not members: they enter only by invitation to a shared conversation and never through these doors. **Bulk invitations:** an inviter permitted under NOOK-060 pastes many addresses or uploads a CSV of email, name, role (member or guest) and channels, with an optional expiry for guests, up to 1,000 rows per batch. Before anything is sent, a preview flags existing accounts, already-pending invitations, duplicates within the batch, invalid addresses and addresses refused by domain policy; one action then sends one invitation per remaining address. Pending invitations can be resent or revoked and expire after 30 days by default, and each accepted invitation lands the person in the NOOK-060 default channels plus any channels named for them. **Invitation link:** owners, or admins delegated under NOOK-060, create a shareable link with an expiry (up to 30 days) and a use limit (up to 400 uses, Slack's bar). Each use still confirms the joiner's mailbox, creates a member (never a guest or admin) in the default channels, and is audited under NOOK-051 with the link ID. A use past the limit or expiry is refused, revoking a link stops further joins immediately, owners can disable links for the workspace, and links are disabled while SSO is enforced. **Approved-domain join:** owners approve email domains verified by a DNS TXT record; consumer webmail domains such as gmail.com cannot be approved. Anyone who confirms a mailbox at an approved domain joins as a member, never a guest or admin, into the default channels and under NOOK-069 second-factor policy; while SSO is enforced, domain joins go through the IdP. Owners can switch a domain off without affecting existing members. **Invitation requests:** where members may not invite, a member may request an invitation for an address, which an authorized inviter approves or declines. Team access grants distinguish members from channel-scoped guests. Channel membership and fixed DM audiences remain independent of administrative roles. Deactivation revokes every session and private read; reactivation never revives old sessions or silently regrants private conversations.

  **Acceptance evidence:** Invite a member and scoped guest via Team, confirm once, browse/join as each and refuse guest expansion or owner DM snooping. Deactivate a person signed in twice, reactivate, and prove both old sessions remain invalid and explicit regrant is required. **Bulk, link and domain doors:** paste 50 addresses including one duplicate and one invalid; the preview flags both, 48 invitations are sent, and each person who accepts lands in the default channels. Create a 7-day invitation link limited to 10 uses: the 11th use is refused, revoking the link stops further joins, and each use appears in the audit log. Approve a verified `example.com`: a new `@example.com` person joins as a member with 2FA enforced, while an `@other.com` address and an attempt to approve `gmail.com` are refused. With member invitations restricted to admins, a member's invitation request reaches an admin, who approves it, and the invitee joins.

- **NOOK-019 — Search conversations and files.** Search native and imported messages, thread replies, authorized file names and extracted text with channel, person, date range, conversation type and has-file filters. Matching combines word/token search for terms of any length (one- and two-character terms such as "Q3", "HR", "PO", "AI", "5k" and single letters or digits included) with exact phrases, prefix and case-insensitive substring search, Unicode-aware tokenization and readable query errors; no term is refused for its length. Results identify source, timestamp, author and excerpt, open surrounding context, and page completely. Index, previews, snippets and counts recheck current membership and deletion/retention; unsupported or unindexed files are named rather than represented as searched. **Typed modifiers:** Slack's search modifiers parse into the same filters, shown as removable chips: `from:@person` and `from:me`, `in:#channel` and `in:@person`, `with:@person`, `to:me`, `has:link`, `has:file`, `has:pin`, `has::emoji:` (a reaction), `is:thread`, `is:saved`, `before:`, `after:` and `on:` with a date, `during:` with a month or year (for example `during:september`, in the searcher's time zone), quoted exact phrases and `-word` exclusion; an unrecognized `word:` is searched as text with a hint listing the supported modifiers. **Files views:** every conversation has a Files tab listing its native and imported files newest first, filterable by type and person, and a workspace Files view lists every file the member can read; both recheck current membership, deletion and retention like search.

  **Acceptance evidence:** Find an old imported reply and a phrase inside a permitted PDF/text attachment using author/date/channel filters; open context from each client. Search "Q3" and "HR" with a channel and date filter and get every authorized message and file containing those tokens. Page an 80,000-message corpus completely with no duplicate or missing result; remove membership/delete a file and confirm its snippets, counts and cached results disappear. Type `from:@jane in:#sales has:link before:2026-10-01` and get the same results as the equivalent filters, shown as chips; `during:september` returns September messages in the searcher's time zone, and `-draft` excludes matching messages. Open a private channel's Files tab and see its native and imported files; a non-member sees neither the tab nor those files in the workspace Files view.

- **NOOK-020 — Saved items and channel pins.** People save messages, files and links privately for later, optionally attach a due reminder, and retrieve an ordered personal list. Authorized members pin/unpin shared context in a channel with visible actor and time. Saves and pins preserve contextual links, honor current ACL and tombstone unavailable sources without copying erased bodies.

  **Acceptance evidence:** Save a DM and pin a channel message, retrieve both after restart on another device, then remove membership and erase the source. Private saves stay private and every unavailable item loses its excerpt.

- **NOOK-021 — Native clients and dependable device catch-up.** Ship signed iOS and Android apps and macOS, Windows and Linux desktop apps, alongside the web/PWA, with authenticated deep links, multi-workspace isolation, keyboard/screen-reader access, update/recovery paths and explicit supported OS versions. A device's first sign-in, the open-in-app links and QR codes that point it at this installation, and the MSI/MSIX, PKG, Linux and MDM-configured packages IT uses to deploy it are NOOK-074. APNs and FCM power native notifications, badges and call invitations, reached from every customer installation through NOOK-078's push path (the publisher-operated Nook Push Gateway, or the owner's own credentials with self-built apps), with per-device preferences, the message-text previews, org caps and Reply/Mark-as-read actions defined in NOOK-062. An OS share-sheet/share-target extension on iOS, Android and desktop shares photos, files, links and text from other apps into a chosen conversation or thread with an optional note, under current membership, NOOK-061 file policy and NOOK-066 posting permission. Encrypt authorized offline caches, make cached age/offline state visible, queue drafts/sends with intent identity, and reconcile history, edits, deletions, read state and revoked scope on reconnect. Logout clears local private data; an offline device cannot learn revocation until reconnect, and admins can disable offline retention. Show OS permission, registration, provider acknowledgement and observed device receipt distinctly; repair stale subscriptions without duplicate sends. **Default alerts:** first sign-in on a device asks for OS notification permission, saying what will alert; once granted, and without opening settings, a new member's device alerts on DMs and group DMs, mentions (including NOOK-065 broadcast mentions), keyword hits, call invites and replies in followed threads (including NOOK-072 automatic follows), matching Slack's defaults; which of the member's devices alerts while they are active on a desktop is NOOK-073. Per-conversation `all new messages`/`mentions`/`nothing` levels and their mobile override come from NOOK-032, organization defaults from NOOK-060, and settings show plainly when the OS has denied permission. **Supported OS floor:** the current and previous major releases of iOS/iPadOS and Android (Android with Google Play services for FCM), macOS, Windows 11, and the current and previous Ubuntu LTS, published in `/docs` and on the download page and re-stated each release; an install or sign-in below the floor shows an explicit unsupported notice instead of silently degraded alerts. **Delivery bar, measured and published:** for events eligible to alert after NOOK-032 schedules, NOOK-062 previews and NOOK-065 broadcast policy (mentions, DMs, keyword hits, broadcast mentions, call invites) to a device reachable by APNs or FCM (or a running/at-login desktop app) and routed to that device by NOOK-073, at least 99.5% are displayed; the bar is measured against that routed device set, and a release after a NOOK-073 hold is timed from the release; commit-to-display is p95 ≤ 5 s and p99 ≤ 30 s; a call invite rings a locked phone (CallKit/PushKit on iOS, a full-screen call notification on Android) within 5 s at p95; and every device's badge equals the authoritative unread plus Attention count (followed-thread replies included unless the member chose NOOK-072's mentions-only thread badge) within 10 s of reconnect, foreground or a read on another device. Every eligible event not displayed is recorded under NOOK-008's distinct facts as `provider-unconfirmed` (the NOOK-078 gateway, APNs or FCM did not accept or acknowledge it) or `device-unobserved` (accepted, but no display receipt from the device within 60 s), shown in the member's device delivery view and the owner's delivery report (NOOK-048); none goes missing silently. A mobile alert held because the member was active on desktop and then cancelled by a read or done is recorded as `held-active-elsewhere`, counted neither as displayed nor as a miss. The delivery bar is an obligation of every customer installation on its own Cloudflare account, never met only by a publisher-operated installation.

  **Acceptance evidence:** Install production-signed apps on real iOS/Android and each desktop OS and sign each in through a NOOK-074 path, never a pre-seeded session. In foreground, background, force-closed and locked states send a mention/DM/call invite, observe alert/badge and tap to context; disable permission and expose the reason. Read on another device, disconnect/reconnect, race a send retry and revoke membership: converge once, purge revoked caches and verify logout isolation. Share a photo and a PDF from the iOS and Android share sheets into a private channel thread and see each post once. **Default alerts:** a freshly invited member installs the iOS app and allows notifications without opening settings; a DM, a mention and a keyword hit each alert once; repeat on Android and on one desktop OS. **Delivery bar:** on a real-device fleet of at least two iOS and two Android phones at the published OS floor plus each desktop OS, send 500 mixed eligible events (mentions, DMs, keyword hits, broadcast mentions and call invites) spread across foreground, background, force-closed and locked states over 24 hours. Record each event's server commit time and device display time from device logs, then compare to the bar: ≥ 99.5% displayed, p95 ≤ 5 s and p99 ≤ 30 s commit-to-display, locked-phone call ring ≤ 5 s at p95, and every badge equal to the authoritative unread plus Attention count within 10 s of reconnect. Every undelivered event appears as `provider-unconfirmed` or `device-unobserved` in the device delivery view and owner report; a single silent miss fails the journey. Install on a device below the floor and see the unsupported notice. A web-Push test alone cannot satisfy this journey. **From a customer installation:** the fleet signs in to an installation on a fresh Cloudflare account that is not the publisher's and is reached only through NOOK-078's push path; a run against a publisher-operated installation does not count. **Automatic follows and active-elsewhere routing:** a reply in a thread the member authored alerts their phone without any explicit follow while they are not active on desktop; while they actively use the macOS app the same reply alerts only the desktop, and their phone alerts after the screen locks unless it was read first. Events held for active-elsewhere in the 500-event run appear as `held-active-elsewhere` or are timed from release, never counted as displayed or silently missing.

- **NOOK-022 — In-context voice, video and screen sharing.** Start a one-click audio/video huddle in any authorized channel or DM, discover/join the ongoing call, invite current participants and move between supported clients. Support device selection, mute, camera, screen/window sharing, participant roster, hand raise, captions, keyboard controls, reconnect and explicit end/leave. Supported size: up to 50 participants per huddle with up to 25 cameras on at once (Slack's paid bar), several simultaneous screen shares and grid/speaker layouts that adapt at 390 width; above the limit a joiner sees an honest "This huddle is full (50 of 50)" state with an option to be told when space opens, never a silent degraded join. Use integrated media infrastructure such as LiveKit Cloud or Twilio with scoped short-lived tokens, disclosed media processing and TURN relay operation. Joining, guest entry, screen content and call artifacts follow current conversation/org policy; revocation terminates admission. Recording/transcription is off until participants receive notice and policy-required consent; NOOK-024 governs artifacts.

  **Acceptance evidence:** From a private channel and DM run a three-person real audio/video call across desktop and phone, share a window, read captions, switch devices, interrupt networking and reconnect. Remove a participant and deny further media access; an outsider cannot join with a copied link. Refused microphone/camera permission and failed relay show actionable states. At 50 participants with 25 cameras on across desktops and phones, measure p95 join time ≤ 3 s, audio continuity (no gap over 500 ms in a 10-minute session on the target network) and screen-share legibility (10-pt text readable at 390 width); a 51st joiner sees the full state.

- **NOOK-023 — Zoom, Google Meet and Teams meetings.** Connect Zoom, Google Workspace/Meet and Microsoft 365/Teams with least-privilege OAuth. Create or attach a provider meeting from a conversation/calendar event, render who hosts it and its time zone, and launch/join from every client. Provider authentication and admission still govern the call. Changed/cancelled events update their card; missing scopes, revoked grants and failed meeting creation are visible without posting a broken success link. Recording links inherit both provider and Nook permissions.

  **Acceptance evidence:** For each named provider connect a test tenant, create and join from a channel and DM on phone/desktop, reschedule then cancel, and revoke consent. Observe provider-confirmed links and honest failure; a private recording link never broadens provider access.

- **NOOK-024 — Asynchronous clips, transcripts and call artifacts.** Record/upload audio, video and screen clips with playback controls, captions, accessible transcript and a source message; show upload/transcode failures and supported limits. Call recordings, consent records and transcripts remain scoped private files with retention, export and hold policy. Users may correct transcript text with history; AI summaries cite timestamps and remain suggestions. No recording starts secretly, and deleting a source invokes the applicable preservation/erasure policy.

  **Acceptance evidence:** Record a clip from phone and desktop, play with captions and seek from transcript; record a consented huddle and inspect its audience. Reject an outsider, simulate failed processing, correct a transcript and export/delete or hold the artifact without losing policy state.

- **NOOK-025 — App and bot platform.** Owners install/revoke scoped apps with visible publisher, permissions and egress. Provide versioned REST/events APIs, incoming and outgoing signed webhooks, slash commands, interactive message buttons/forms and bot identities; the Slack-compatible webhook, Web API, Events API, slash-command and Block Kit surface with its published compatibility matrix is NOOK-063, Zapier/Make/n8n connectors are NOOK-064 and app link unfurls are NOOK-068. Event cursors, signatures, replay protection, idempotency, rate limits, subscription filters and delivery/failure logs are public contracts. Bots never impersonate people, accept commitments or gain a private conversation without a grant. **App DMs and app home:** an app whose install-time permission screen lists the `im:write`-style scope may open exactly one app DM per person and app (NOOK-063 `conversations.open`), labelled with the app identity, publisher and an App badge, holding only that app's messages and the person's replies; owners allow app DMs per app or require admin approval. The person can mute the app DM, leave it (hiding it until the app next posts) or block the app, after which opens and posts are refused and nothing is delivered until they unblock; every app DM and its scopes are listed in the person's Apps settings. An optional app home (`views.publish`) shows app-supplied blocks to that person only. An app's token reads only its own app DMs and conversations it is a member of; it never reads or lists another DM, group DM or private channel. A person grants an app a private channel only by adding it as a member (`/invite @app` or channel details), which members see and NOOK-051 audits; removing it revokes access immediately. **Channel management:** creating, inviting into, renaming, setting topic/purpose and archiving channels requires the app's install-time channel-management scope, follows the org's channel-creation policy (NOOK-060), invites only people the org permits, and every use is audited with the app identity and the installing person. Outgoing actions bind actor, conversation, destination and consent; secrets stay server-side, rotated/revoked grants stop new work and untrusted links cannot cause arbitrary fetch/SSRF.

  **Acceptance evidence:** Install an app as owner, subscribe only one channel, post a signed incoming event, invoke a slash command and click an interactive approval. Replay/forge payloads, exceed a rate limit, copy a private event ID and revoke the app: deny escalation, avoid duplicate effects and show delivery/uncertainty with secrets redacted. An app with the DM scope opens one app DM with a member; the member mutes, blocks and unblocks it, and posts are refused while blocked. The member adds the app to a private channel and removes it, and the audit log shows both; the app's token cannot list or read any other DM, group DM or private channel. An app without the channel-management scope cannot create a channel.

- **NOOK-026 — Workflow builder and durable scheduled work.** Non-developers build versioned workflows triggered by schedules, forms, messages, an emoji reaction added to a message, a member joining a channel, webhooks and accepted commitments, with conditions and scoped steps for messages, requests, member pairing and connector actions. Templates ship for a welcome DM when someone joins a channel, an action on a chosen reaction (for example :ticket: opens a request), recurring stand-ups and intro pairing (members of a channel paired at random on a set cadence and sent a prompt by DM). Preview audience/data/actions, test with disclosed effects, publish as an accountable owner and choose approvers. Every run records trigger, input references, revision, owner, due time, steps, approval, retries, outcome and stop condition; wait states survive restart. Pause/cancel prevents future work, approval expiry stops writes and dead/uncertain external effects stay visible without blind replay. Editing a workflow never rewrites an in-flight agreement.

  **Acceptance evidence:** Build a recurring stand-up form that posts a channel summary and proposes work only for explicit acceptance. Publish, trigger once, pause during an approval wait and restart; no duplicate post or unauthorized write occurs. Refuse a revoked connector and expose the failed step plus a safe human resume choice. A welcome workflow DMs a person exactly once when they join `#onboarding`; adding :ticket: to a message runs the reaction workflow once for that message even when two people add it; intro pairing over a 9-member channel produces four pairs and one trio with no one left out.

- **NOOK-027 — Engineering and project integrations.** First-party GitHub, Jira, Asana, Linear, Trello, ClickUp, monday.com and Vector connectors post selected issue/PR/review/build/deployment/project changes to chosen channels with source links and deduplicated provenance. Authorized slash commands/cards can inspect, propose or create linked issues/tasks and request review, then refresh status through completion/reopen. Each external write previews its exact effect and uses the acting user’s scoped grant; failure/revocation is visible. Vector remains the project-work product; Nook owns the conversation and accepted commitment, not a duplicate tracker or roadmap.

  **Acceptance evidence:** Connect each named service, route a selected project/repository event, act from a card and follow a linked issue/PR from open to complete to reopened. Retry its webhook, remove source permission and revoke the grant: exactly one authorized effect and no leaked project context. Vector remains the authoritative work record.

- **NOOK-028 — Support, sales and operational signals.** First-party HappyToHelp, Zendesk, Salesforce and HubSpot connections bring authorized ticket/account/contact/deal context into scoped conversations and send reviewed replies/notes or create linked work through scoped grants. Uptime, Lamp, PagerDuty and Opsgenie integrations route incident, on-call page, recovery and alert signals with source severity, acknowledgement state and stable links, thread follow-up updates, deduplicate repeats and expose delivery failures. Users choose routes, urgency, quiet-time behavior and pause. HappyToHelp owns support cases, Uptime/Lamp/PagerDuty/Opsgenie own their incident/alert/on-call state, and Salesforce/HubSpot own CRM records; a Nook read/reaction never claims customer reply, incident resolution or human response.

  **Acceptance evidence:** For each named service route a real sandbox case/account/alert event, inspect an authorized preview, approve a supported write and observe source-confirmed state. Deliver duplicate/recovery events, disconnect the source and remove channel access; keep one linked thread, no false completion and a visible failed/uncertain action.

- **NOOK-029 — Connected documents and knowledge.** Google Drive, Microsoft 365/OneDrive/SharePoint, Notion, Dropbox and Box connections, plus Loom video and Figma design links, provide permission-aware link cards, document/file picking, comment/update signals and authorized retrieval for search/agents. Source ACL and current Nook audience intersect; private metadata/previews never leak through a shared link. Expired links or revoked grants lose previews and derived access. File storage, collaborative authoring and long-lived knowledge remain in their source product or the separate Chest product; Nook does not become that document repository. No automatic remote fetching occurs without explicit connector policy.

  **Acceptance evidence:** Connect each named provider, share an authorized document into a matching channel, retrieve its card/text as an allowed member and refuse a source-denied member. Revoke provider sharing or disconnect, refresh search/agent context and show unavailability without cached excerpts or broadened access.

- **NOOK-030 — Calendars, availability and meeting reminders.** Google Calendar and Microsoft Outlook/Exchange Online calendars show scoped event cards, local time zones, reminders, free/busy and optional meeting status. With an explicit grant, users create/update events and attach Meet/Zoom/Teams links from a conversation, invite chosen people and receive change/cancellation notices. Private event titles and attendee details remain hidden where only availability is authorized. Members may turn on calendar-driven status: during a busy event their status becomes "In a meeting" ("Out of office" for an all-day out-of-office event) with the event's end as its expiry, set and cleared from the calendar without revealing the event title, and never overwriting a status the member set by hand. Quiet time and deduplication apply, and revoked access stops polling, sync and writes.

  **Acceptance evidence:** Connect both calendars, create a cross-time-zone meeting from a DM, accept/change/cancel it at the provider and see exactly one current card/reminder. Show free/busy without private title access, cross a daylight-saving transition and revoke the grant before a scheduled action. Turn on calendar-driven status: a private meeting sets "In a meeting" without its title and clears at its end, and a hand-set status is left untouched.

- **NOOK-031 — Shared channels between organizations.** Separate organizations explicitly invite and mutually approve a shared channel with named org admins and an external-people indicator. Each org independently grants its people, apps, guests, export, retention and media permissions; the effective cross-org data/action policy is shown before connection. No shared channel reveals either parent directory, internal channels or DMs. A member may also start a DM or group DM with an individual at another organization when both organizations' policies allow it and the recipient accepts the invitation; external DMs carry the same external indicator, per-org retention/export/media policy and either-side block or disconnect. Scope changes require authorized approval; either org can disconnect, stopping new access and app delivery while retained copies/holds and each org’s erasure duties are disclosed and auditable. A person whose organization runs no Nook installation joins a shared channel or external DM as a NOOK-075 external participant, and Slack Connect conversations move at cutover under NOOK-076.

  **Acceptance evidence:** Two independently administered organizations approve a channel, exchange messages/files/calls and install one narrowly scoped bot; start and accept an external DM, then block it through one organization's policy. Exercise a single-channel guest, denied directory lookup, conflicting retention/export policy, member removal and full org disconnect. Every client/search/agent/API loses revoked access while permitted historical copies are explicitly accounted for.

- **NOOK-032 — Recurring quiet schedules and notification policy.** Extend per-conversation mute/follow and one-off quiet time with recurring working/quiet hours in the recipient’s time zone, weekday exceptions, vacation and device-specific rules; when the member's phones and tablets alert relative to desktop activity is NOOK-073. Members choose mentions, DMs, followed threads, digests and whether explicit urgent requests may bypass quiet time. Activity feed, badges and external channels reconcile read/done status without turning a mention into an obligation. Members define personal keywords (words or phrases, case-insensitive, token-matched like NOOK-019) that create a `keyword` Attention reason with its own push/email choice, only in conversations they can currently read, never for their own messages and not in muted conversations unless chosen. Explain why each item was delivered, held or suppressed and allow a bounded snooze; DST and travel do not silently shift the declared schedule. **Per-conversation notification level:** each member sets any channel, private channel, shared channel or group DM to `all new messages`, `mentions` or `nothing`, with an optional different level for mobile devices (phones and tablets) than for desktop and web. `mentions` is the default for channels; one-to-one and group DMs default to `all new messages`. `all new messages` creates a routine `all-messages` Attention reason and alerts the chosen device class on each new root message and each thread reply also sent to the channel (NOOK-070), under the member's quiet schedule, NOOK-021's delivery bar, NOOK-062 previews and the NOOK-014/NOOK-015 gathering and ten-minute folding rules; the member's own and imported messages never alert. `mentions` alerts only on explicit mentions, NOOK-065 broadcast mentions, keyword hits, urgent requests and followed-thread replies (including NOOK-072 automatic follows). Alongside the level, a member may set **all thread replies** for the conversation (NOOK-072), so every thread in it counts as followed for them, with its own mobile override. `nothing` sends no device or email alert for that conversation, including `@channel`, `@here`, `@everyone` and explicit mentions, consistent with NOOK-065's ignore choice; explicit mentions and urgent requests still appear silently in Attention. `all new messages` and NOOK-013 mute are exclusive: choosing one clears the other. A settings list shows every conversation whose level differs from the default (per device class), and each row resets to the default in one action. The level is served through `GET/PATCH /api/conversations/:id/notify {level, mobileLevel, threads, mobileThreads}` and every client. **New-member defaults:** push alerts on DMs, mentions, keyword hits, call invites and followed-thread replies, including NOOK-072 automatic follows (NOOK-015, NOOK-021), routed between desktop and mobile by NOOK-073. Email stays off (NOOK-014), and first sign-in onboarding says so plainly, offering one action to turn on "Email me about mentions and DMs when I'm away" bundled every 15 minutes or hourly; owners may make that email choice on by default for new members under NOOK-060.

  **Acceptance evidence:** Set a weekday quiet schedule and channel override, cross midnight/DST and restart without a page open. Send routine/mention/urgent events and a message containing the recipient's keyword "invoice" from another account (one keyword item in a readable channel, none in a muted or unreadable one), read on a second device and compare the activity feed, held items, badges and provider attempts with the recipient’s stated rules. **Per-conversation levels:** a freshly invited member allows notifications without opening settings, and a DM, a mention and a keyword hit each alert once. They set `#incidents` to `all new messages` on mobile only: a colleague's plain message there alerts their phone but not their desktop, while a routine message in a `mentions` channel alerts nothing. Setting `#random` to `nothing` suppresses even an `@here`. During quiet time the `#incidents` alerts are held and released by the schedule. The exception list shows `#incidents` and `#random`, and resetting each returns it to `mentions`. First sign-in tells the member that email is off, and its one-action opt-in produces one 15-minute bundled email for a mention and a DM while away. **All thread replies:** with `#incidents` also set to all thread replies on mobile only, a reply in a thread the member never touched alerts their phone (when they are not active on desktop, NOOK-073) and not their desktop, the exception list shows the setting, and resetting it returns `#incidents` to followed threads only.

- **NOOK-033 — Personal and channel reminders.** Create a personal reminder from plain text or a message/file, or schedule a channel reminder visible to its intended audience, with due time, time zone, owner and optional recurrence. Edit, snooze, complete and cancel independently of message reads and commitments. Persist due work through restart, recheck audience at firing time and show delivered/held/failed outcomes. A reminder neither assigns accepted work nor automatically reports its completion. **Natural-language times:** the `/remind` command and the reminder dialog parse Slack's plain-text grammar in the creator's time zone: a target (`me`, `@person` or `#channel`), a time (`in 20 minutes`, `at 3pm`, `tomorrow at 9am`, `on October 20`, `next Tuesday`) or a recurrence (`every weekday at 9am`, `every Monday`, `every month on the 1st`), and quoted or `to …` text. The parsed time and recurrence are shown for confirmation before saving, and a phrase that cannot be parsed is asked again rather than guessed. Reminding another person or a channel follows NOOK-060 policy and the target's current membership.

  **Acceptance evidence:** Schedule a personal message reminder and recurring channel reminder, close every client and restart before due. Observe one eligible reminder at the chosen time, snooze/edit/cancel it, revoke the creator’s channel access and confirm no unauthorized later post. `/remind #standup "Post your update" every weekday at 9am` shows its parsed recurrence and posts at 9:00 in the creator's time zone on weekdays only, across a DST change; `/remind me in 20 minutes to call Ana` fires once 20 minutes later; `/remind me sometime soonish` asks again and saves nothing.

- **NOOK-034 — Scheduled and recurring messages.** Compose a message for later or a recurring channel prompt with previewed audience, attachments, time zone, recurrence, owner and end date. The owner can inspect pending instances, edit/cancel future occurrences and pause the series. A durable scheduler sends once per occurrence under current identity/membership; catch-up policy is explicit after outages and stale prompts need review. Failures, expiry and external uncertainty are visible, never silently skipped or reposted.

  **Acceptance evidence:** Schedule a one-off and weekly stand-up, change the time across DST, restart at due time and replay a scheduler claim. See one post per occurrence, then pause/cancel and remove posting access: no new post or hidden failure.

- **NOOK-035 — Channel recaps and catch-up digests.** On request or a member-approved schedule, summarize authorized channels/threads and unread periods with links to messages/files, decisions, accepted obligations and unresolved questions. Recaps show range, omissions, freshness and uncertainty; distinguish proposals from agreements and reports from verified outcomes. A recipient sees only their current source intersection, including in shared channels; digest schedules respect quiet time, opt-out and cost controls. People can correct/dismiss a recap without rewriting source discussion.

  **Acceptance evidence:** Request a channel/thread recap and daily unread digest over contradictory discussions and inaccessible channels. Check every factual point against a source link, remove access before delivery, correct a claim and cancel the schedule. No private snippet or invented acceptance survives.

- **NOOK-036 — AI search with cited answers.** Ask questions across conversations, files and explicitly connected company context. Retrieve only current-authorized content, cite exact passages/messages and timestamps, open originals, identify conflicting/stale evidence and say when no support exists. A file or message may contain hostile instructions but cannot grant a tool or change system policy. User feedback/corrections remain separate from source edits; answer caches and embeddings inherit retention, erasure and revocation, and no answer declares completion or agreement.

  **Acceptance evidence:** Ask an answerable, contradictory and unanswerable question with private files and a hostile document in the corpus. Follow citations, revoke a source and repeat as a different member: deny cross-scope context, abstain where unsupported and perform no embedded instruction or external write.

- **NOOK-037 — SSO, SAML and lifecycle provisioning.** Owners configure SAML 2.0 and OIDC SSO for Okta, Microsoft Entra ID and Google Workspace, with verified domains, session policy, MFA enforced at the IdP and a protected audited recovery route. SCIM 2.0 provisions/deprovisions users and groups, maps app roles and channel grants explicitly and never auto-grants DMs. Provisioning previews collisions and external identities; offboarding ends sessions, app/agent grants and device access, while preserved work follows retention/hold. Just-in-time sign-in does not bypass invite/domain/org policy. Native apps reach the IdP through the system authentication browser with PKCE, never an embedded web view (NOOK-074).

  **Acceptance evidence:** Use test tenants for all three IdPs, enforce SSO, rotate signing keys and reject wrong audience/issuer/replayed/expired assertions. SCIM-create a group/member, map one channel, suspend/delete and replay the event; every client and grant loses access without deleting preserved evidence. Exercise audited owner recovery without a blanket bypass.

- **NOOK-038 — Retention, legal hold and preservation.** Authorized admins set workspace/conversation policies for messages, versions, files, clips and derived AI data, preview what expires and when, and run/resume durable deletion with failures shown. Authorized custodians place/release a scoped legal hold over people, conversations, date ranges and artifacts with immutable reason/history; it preserves covered revisions and bytes against ordinary edits/deletion/retention. User-visible deletion still removes ordinary access, while preserved copies stay in a separate custodian boundary. Erasure requests blocked by a hold show the lawful policy exception, scope, owner and pending disposition; no completed erasure receipt falsely claims held bytes were removed. Hold release re-evaluates pending deletion without restoring messages to chat.

  **Acceptance evidence:** Apply a short retention policy, hold a selected member/date range, edit/delete held messages and expire their files. Ordinary participants lose deleted content, a custodian preserves exact history, and an unauthorized admin cannot retrieve it. Release the hold and finish the queued erasure with actual byte verification and audited counts.

- **NOOK-039 — Compliance search and controlled exports.** Provide separately granted, audited eDiscovery/compliance search and case exports for authorized public/private/shared conversations, DMs, message revisions, memberships, artifacts and hold state, with filters, approval, purpose, checksum manifest and chain of custody. This explicit exceptional authority is never inferred from owner/admin chat role and never changes ordinary DM access. People can inspect the organization’s export policy; requester, approver and custodian permissions are distinct. Export jobs are cancellable, access-expiring and accounted for; external downloads remain outside Nook erasure. Integrate Microsoft Purview and archive/eDiscovery providers through scoped export, without claiming certification merely from the feature.

  **Acceptance evidence:** Open a case, obtain required approval, export a held DM/file plus revision history and verify manifest completeness and hashes. Try the same through an ordinary admin session and deny it. Revoke the custodian mid-job, expire the download and inspect a durable audit of search, approval and retrieval.

- **NOOK-040 — Information protection and data handling.** Admins label sensitive conversations/files, restrict external sharing/download/app/agent use and apply reviewed DLP rules with block, warn or quarantine behavior. Explain the rule and authorized override, preserve an appeal/audit trail and recheck policy on every connector/export/AI path. Encrypt transport and stored credentials, disclose hosting/subprocessors and AI retention/training policy, and keep customer content out of model training unless explicitly opted in by the organization. Device/offline protections cannot retract a copy already downloaded; policy never promises that they do.

  **Acceptance evidence:** Label a private channel, attempt a restricted external share, connector post, export and AI retrieval, and observe the configured block/warning. Exercise an approved override and revoke it; inspect policy history, secret redaction and deletion of derived content after source erasure.

- **NOOK-041 — Slack migration and cutover.** Import official Slack export ZIPs within the exporter’s actual authority, including public/private channels, one-to-one/group DMs and shared-channel history where supplied. Preview identities, memberships/audience, date coverage, messages, threads, reactions, edits/deletions, pins/saves, bot provenance and file bytes/references with explicit unsupported/missing accounting. Map identities and private audiences deliberately, never by display-name collision. Fetch permitted files using a scoped approved source grant or supplied bytes; list inaccessible files and require a reviewed disposition. Resume/dedupe by source identity, reconcile every record and retain immutable provenance. Imported history creates no unread, notifications, bot/workflow runs or accepted work. Guarded batch undo preserves later native discussion. The preview lists every app, bot, incoming webhook, slash command and workflow found in the export (`integration_logs.json`, bot messages and bot profiles, and workflow data where supplied) with its Nook disposition: repoint to a NOOK-063 Slack-compatible webhook or app (issuing the new URL/token), replace with a named first-party connector (NOOK-027–030), replace with a NOOK-064 Zapier/Make/n8n path, rebuild as a NOOK-026 workflow, or unsupported with its reason. Third-party-hosted Marketplace apps the buyer cannot repoint get a named Nook replacement where one exists: Polly and Simple Poll map to NOOK-070 polls, Geekbot and Standuply to the NOOK-026 stand-up template, Donut to the NOOK-026 intro-pairing template, and reminder and birthday apps to NOOK-033 reminders or a NOOK-026 template. Cutover yields an owner checklist and no integration is silently dropped. Source export-plan restrictions are shown, never solved by making private content public. **Consented member source for what the export omits:** Slack's official export on Free and Pro plans contains public channels only (private channels and DMs need Business+ after applying, or Enterprise), so for every conversation the supplied export does not include, each member may sign in to Slack through Nook with a user-token OAuth grant limited to reading their own history (`groups:history`, `im:history`, `mpim:history`, `files:read`, with `users:read` for identity mapping), shown before consent with exactly what will be read. Nook fetches only the private channels, DMs and group DMs that member belongs to (`users.conversations`, `conversations.history`/`replies`, file bytes through that token) into a staged preview that only that member can see; no owner, admin or other member reads staged content, and an admin role never consents for anyone. A staged conversation is published to its Nook audience only when its audience rule is met: a 1:1 DM or group DM needs every participant's consent (their own grant or an in-Nook consent on the staged item); a private channel needs its Slack channel manager's consent or, where none is in Nook, the member quorum the owner states before the run (default: a majority of its current members) with no explicit objection; and it imports to exactly its Slack membership through reviewed identity mapping. File bytes, threads, reactions, edits/deletions, provenance (`slack-member-grant` and the granting member), resume and dedupe by source ID (overlapping fetches from several members yield one record), inert import and guarded batch undo apply exactly as for the export. Revoking a member's Slack grant, in Nook or at Slack, stops further fetches with that token at the next request; already published conversations stay, and that member's unpublished staged items are erased unless another consenting member's grant covers them. Staged content that never reaches its audience rule is erased after an owner-set window (default 30 days). The cutover report lists every conversation not imported by type, member count and missing consents; nothing is silently dropped. Through the owner's approved source grant, custom emoji (`emoji.list` with image bytes and aliases) and user groups (`usergroups.list` and `usergroups.users.list`) are imported so custom-emoji reactions render and `@group` mentions map to Nook user groups (NOOK-065). **Cutover invitations:** the cutover preview lists every active Slack member from the export's `users.json`, or from the owner's approved source grant with `users:read.email` where the export lacks addresses. Each row shows the Slack user ID, email, name and a proposed Nook role. Full members are proposed as members, and Slack Workspace Owners and Admins are flagged so the owner may choose admin; admin is never granted without that explicit choice. Multi- and single-channel guests (`is_restricted`, `is_ultra_restricted`) stay guests limited to their mapped channels. Deactivated users (`deleted`) and bots get no invitation, while their history still imports under their reviewed identity. A member with no address is listed as needing one, never guessed. After editing roles, the owner sends every invitation in one reviewed action, now or at a chosen cutover time that survives restart. Accepting an invitation claims that person's imported authorship through the reviewed Slack-user-ID mapping recorded on the invitation, never by display-name match, and opens the consented member source to them. The cutover report shows invited, joined, pending and bounced counts per role and resends pending invitations in one action. Joining changes no private channel or DM audience, and invitation emails carry NOOK-074's code, open-in-app link and QR code. Slack Connect participants from other organizations are not in `users.json`; their channels and external DMs get their own preview, dispositions and report under NOOK-076. **Personal setup:** through a member's consented grant, Nook also imports what Slack lets that member read for themselves: their reminders (`reminders.list`, `reminders:read`) as NOOK-033 reminders (future ones fire at their time, past ones import as completed) and their starred items (`stars.list`, `stars:read`, where Slack still returns them) as NOOK-020 saved items, visible only to that member. Onboarding and the cutover report state plainly which personal setup Slack gives to no export or API and must be set again (sidebar sections, per-channel notification levels and mutes, scheduled messages, and saved "Later" items the API does not return), with a checklist linking each to its Nook setting. **Slack links in imported history:** every Slack message permalink (`https://<workspace>.slack.com/archives/<channel>/p<ts>`, with or without `thread_ts`), `slack://` link and `<#C…>`/`<@U…>` reference in imported messages, pins, bookmarks, topics and purposes is resolved through the import's Slack-ID map and rendered as the Nook permalink of the imported message, thread, channel or person; the original URL stays in provenance and the imported revision is unchanged. A link whose target was not imported, or whose target the reader cannot currently read, renders as "Slack message not imported" (or "Not available to you") with the original URL and never reveals the target's content, name or audience. A Slack permalink pasted into Nook after cutover resolves the same way under the reader's current ACL, and the cutover report counts resolved and unresolved links. **Final catch-up:** everything posted, edited, deleted or created in Slack between the first import and the switch is brought across by NOOK-077's delta passes; the cutover time above runs NOOK-077's final pass before the invitations are sent, and the report states the time through which Slack history is imported.

  **Acceptance evidence:** Use an authorized real Slack workspace export plus synthetic adversarial coverage, preview without writes, resolve an identity collision and a missing file, import public/private/DM/shared histories, verify complete counts/bytes/threads/search and deny outsiders. Interrupt/resume and undo a batch after native activity, confirming refusal where undo would destroy that activity. The preview lists every integration in the export with its disposition, and repointing one incoming webhook delivers to the mapped channel. **Slack Pro consented path:** on a Slack Pro test workspace with a private channel, a 1:1 DM, a three-person group DM, custom emoji, a user group and files, import the official public-only ZIP, then have members grant their Slack user tokens. Two of three group-DM members consent: the group DM stays unpublished and the cutover report lists it with its member count and the missing consent. Both 1:1 participants consent: the DM imports with its threads and file bytes and only those two can read it. The private channel's manager consents: it imports to its exact membership. Revoke one member's grant mid-run and see no further fetch with that token in the Slack request log. An outsider, and the owner and an admin who are not members, find none of this content by search, link, export or API. Custom-emoji reactions and an `@group` mention render after import. **Cutover invitations:** import a Slack Pro export with 120 active members, 3 guests and 5 deactivated users. The preview proposes 120 member and 3 guest invitations and excludes the 5 deactivated users and every bot. Send them in one action at a scheduled cutover time across a restart. An accepting member sees their imported messages attributed to them, a guest sees only their mapped channels, and the report shows joined and pending counts. No private channel or DM audience changes as people join. The integration checklist for an export containing Polly, Geekbot and Donut names NOOK-070 polls and the NOOK-026 stand-up and intro-pairing templates. **Slack links:** an imported message linking to another imported thread opens that Nook thread; a link to a private channel that was not imported, or that the reader is not in, shows "Slack message not imported" or "Not available to you" with the original URL and no content; a Slack permalink pasted after cutover opens the mapped Nook message; the cutover report counts resolved and unresolved links. **Personal setup:** a member with a consented grant finds their future Slack reminders as Nook reminders that fire on time and their starred messages as saved items, visible to no one else; onboarding lists sidebar sections, notification levels, mutes, scheduled messages and Later items as needing to be set again. The Slack Connect journey is NOOK-076's, and the final catch-up journey is NOOK-077's.

- **NOOK-042 — Microsoft Teams migration.** Import an authorized Microsoft Graph Teams extraction with paginated channel, chat and meeting-chat messages, replies, edits/deletions/reactions, users, membership/history-access limits and permitted SharePoint/OneDrive attachments. Record source IDs, timestamps, source tenant, export authority and omissions. Preview all mappings and count reconciliation; unknown historical audience stays unpublished. Source roles/meeting attendance never imply Nook admin or DM membership. Apply the same inert history, resumability, file accounting and safe batch undo guarantees as NOOK-041.

  **Acceptance evidence:** Extract a real test tenant containing channels, private/shared channels, 1:1/group/meeting chats and protected files; page every source set, preview, import and verify counts, chronology and visibility. Revoke an export scope, interrupt/resume and undo without deleting later native work; show each omission.

- **NOOK-043 — Google Chat migration.** Import supported Google Takeout and authorized Workspace administrator exports of spaces, group/individual DMs, threads, membership and available attachments. Record organization/source scope, external-space ownership omissions, author/time, edits and relationships actually supplied. Review identity/audience mappings without merging DMs into discoverable spaces, reconcile all files and retain source provenance. Apply inert publication, resumability/deduplication and guarded undo; unavailable private/external history remains an explicit gap.

  **Acceptance evidence:** Import a real Workspace test export with spaces, threaded replies, DMs and files plus an externally owned space omitted by source policy. Compare coverage and exact visibility, interrupt/resume and undo after native activity, and confirm no notifications, accepted tasks or outsider access.

- **NOOK-044 — Campfire ONCE migration.** Provide version-declared import from a consistent administrator Campfire ONCE backup containing its SQLite snapshot and uploads. Preserve room open/closed visibility, DMs/participant sets, authors, times, replies and file relationships that the source supplies. Reject inconsistent or unsupported schema versions and ambiguous memberships before publication. Preview/reconcile counts and file hashes, map identity without source-password reuse, import inertly with provenance and support resumable apply plus guarded undo.

  **Acceptance evidence:** Take a consistent real Campfire test-instance backup with open/closed rooms, DMs and uploads, preview and migrate it, then compare messages/files and deny unrelated members. Reject an incomplete/unsupported backup and exercise resume and safe undo.

- **NOOK-045 — Zulip migration and topic continuity.** Import a versioned logical Zulip organization JSON/tar export with uploads, channels/streams, topics, subscriptions and private recipient groups. Preserve channel-plus-topic navigation and source links rather than flattening topics into an unreadable transcript. Show whether source scope is public-only, private-with-consent or complete-private and account for withheld data. Passwords/API keys never become destination authority. Use reviewed mappings, inert history, complete reconciliation, resumable deduplication and guarded undo.

  **Acceptance evidence:** Export a real Zulip test organization with two topics in one channel, private conversations and uploads; import and navigate each topic independently, inspect consent omissions and verify private audiences. Resume once without duplicates and undo while preserving later native work.

- **NOOK-046 — Mattermost migration.** Import supported Mattermost bulk JSONL and attachment exports with teams, public/private channels, direct/group conversations, memberships, authors/timestamps, replies, reactions, custom emoji and saved/pinned state where supplied. Preview unsupported/deleted records and deliberate source-role mappings; no source admin becomes a Nook custodian automatically. Reconcile source/file counts and hashes, preserve provenance and use inert resumable apply and guarded undo with private audiences intact.

  **Acceptance evidence:** Export a real Mattermost test deployment spanning two teams, private channels, DMs, replies, saved/pinned items and attachments. Review/map roles, import and find every declared item while denying outsiders; interrupt/resume and undo without removing subsequent Nook messages.

- **NOOK-047 — Readable portability and controlled exit.** Alongside full operational backups, provide user-readable versioned JSON/JSONL and HTML conversation/thread exports with membership history, message versions/tombstones, reactions, decisions, commitment acceptance history, source mappings and checksum-indexed file bytes/links. An ordinary member may export only conversations and DMs they currently belong to. A current workspace owner may export the whole workspace, including private conversations and DMs, only under an explicit, default-off, versioned workspace export policy. Enabling that policy must be announced to every member and recorded in the audit log with who enabled it, when and the policy version before any workspace export includes private conversations. Each workspace export states the policy version it ran under; current ownership and the enabled policy are checked throughout the job. An admin who is not an owner is refused another person’s DM. Protected held copies are excluded and named as excluded without disclosing any hold inventory. NOOK-039 later extends compliance authority beyond this owner policy; neither policy changes ordinary chat access. The readable membership report retains older events whose direction or time was not recorded as "unknown" and records exact joins and leaves from adoption forward, never inventing history. Include workflow/app definitions, non-secret configuration and agent run history as they ship, while naming installation-local credentials, held data and unsupported external artifacts. Provide a stable documented format a buyer can re-import elsewhere and a resumable completeness report; never silently truncate.

  **Acceptance evidence:** Export a member-scoped conversation and all conversations and DMs the member currently belongs to; exclude a private conversation they never joined and one they left, and refuse subsequent access after membership revocation. As a current workspace owner, attempt a whole-workspace export with the default-off policy disabled and refuse it. Enable a versioned workspace export policy: every member receives the announcement and the audit records who, when and the policy version before an export includes other private conversations. Export the populated workspace, private conversations and DMs included, and verify that the export states that policy version; disabling the policy or losing ownership blocks further workspace export access. Attempt another person’s DM as an admin who is not an owner and refuse it. Exclude protected held copies and name that exclusion without revealing any hold inventory. Include an older membership event lacking direction or time: the readable report says "unknown", while exact joins and leaves after adoption retain their recorded direction and time. Read the HTML offline, enumerate JSON/file hashes against the source, cancel/resume a large job, and verify all omissions are named. A successful operational backup alone does not prove readable exit.

- **NOOK-048 — Team administration and reporting.** Authorized admins see membership/guest/app inventory, channel growth/activity, storage, retention jobs, client health, notification delivery classes, failed workflows and connector health over chosen time ranges. Team leaders see accepted/overdue/blocked commitments and unresolved decisions only within their authorized conversations, with drill-through to source and CSV export. Reports disclose denominator, time zone, freshness and missing data; aggregate trends cannot expose private-channel names or DM bodies and do not turn presence/message counts into employee performance scores.

  **Acceptance evidence:** Populate a US 35-person team with guests, a private management channel, missed notifications and overdue linked work. Compare filtered report totals to authorized records, export CSV, remove a viewer’s access and verify both aggregates and drill-through stop exposing that scope; show a failed sync as incomplete data. Repeat totals and drill-through at the NOOK-049 capacity installation (1,000 members, 5,000 channels).

- **NOOK-049 — Reliability, performance and operating recovery.** Serve an owned small/mid-size team without a paid history cutoff, with durable ordered messaging, idempotent retries, reconnect catch-up and explicit pending/error states. **Supported minimum capacity per installation**, measured on the supported hosted configuration: 1,000 members including guests, all concurrently connected on up to 3,000 devices; 5,000 channels plus DMs and group DMs; a largest channel of 1,000 members (the whole organization in its default channel); 7 years and 25 million messages of searchable history plus 2 TiB of files; sustained 50 messages per second organization-wide with 200-per-second bursts. **Objectives at that size:** sent-to-visible p95 ≤ 1 s and p99 ≤ 3 s for online recipients in the same US region, and p95 ≤ 2 s to the last recipient of a 1,000-member channel; reconnect catch-up (history, edits/deletes, unread and Attention) for a member of 200 channels after 24 hours offline p95 ≤ 5 s; search p95 ≤ 1.5 s over the full corpus; 99.9% monthly availability for send and read, measured and published; RPO ≤ 24 h from owned backups (≤ 1 h where the platform's point-in-time recovery is enabled) and RTO ≤ 4 h. People, channel, conversation, user-group and app directories and the channel browser page and search completely at any size, with no 1,000-entry cap. A larger size is supported only when published with its measurement; approaching a platform limit shows a named warning, never silent truncation. Publish measured latency/error/fanout/storage results against these objectives, plus a tested outage/upgrade runbook; do not infer hosted availability from local emulation. Health and monitoring separate chat persistence, fanout, notification providers (the NOOK-078 push gateway, APNs, FCM and browser push services, each named in an outage), integrations, schedulers and media. Dependency outage keeps durable local work inspectable, marks blocked effects and resumes only safe work; no fabricated success or silent loss.

  **Acceptance evidence:** On the supported hosted configuration, seed 1,000 members, 5,000 channels, 25 million messages over 7 years and 2 TiB of files; drive 1,000 connected clients at 50 messages/s with 200/s bursts and a 1,000-member default channel, and measure sent-to-visible p95/p99, last-recipient fanout, 24-hour reconnect catch-up, complete reconnect count and search latency against the stated objectives. Page the people directory and channel browser past 1,000 and 5,000 entries and find the last entry by search. Interrupt fanout/provider access, restart workers, upgrade without data loss, restore from an owned backup within RTO and compare outcomes to the published objectives and component status.

- **NOOK-050 — Owned installation and Base replacements.** Keep Base operator-auth, signed owner claim, portability/erasure journal, mail, editor authority and rer lifecycle guarantees, with Nook’s documented identity mapping, current-membership privacy and private R2 replacements. Setup/doctor name missing configuration without values; dev remains loopback and production excludes development identities. Install, upgrade, export/reset/import and owner-account deployment remain available with Cloudflare Workers/D1/R2/Durable Objects, and current SendGrid, Resend or RER mail configuration. Owners choose the data location for D1/R2 at installation (US by default; EU or another platform-enforced jurisdiction where available), shown by setup and doctor with every subprocessor's region disclosed. Setup, doctor and `/docs` publish the subprocessor list: Cloudflare (Workers, D1, R2, Durable Objects), the configured mail provider, LiveKit Cloud or Twilio where media is configured, Apple APNs/PushKit, Google FCM, the browser push services and the publisher-operated Nook Push Gateway (NOOK-078), each with its region, what it sees, its price and its terms, and owner-credential mode removes the gateway from that list; changing it is a documented export/restore migration. Expose US-first USD operating costs for hosting, storage, mail, media, AI and push/provider accounts without invented capacity or subscription promises. Publish a dated reference monthly USD operating-cost estimate for 50 and 300 active members, with its stated message, storage, media-minute, push, mail and AI assumptions (push includes the NOOK-078 gateway's published price as its own line, and owner-credential mode adds the dated Apple Developer Program and Google Play developer account fees) and computed from measured unit costs each release, beside the dated Slack Pro and Business+ per-seat list prices it compares with; the installation shows its actual monthly cost per active member against that estimate. Stable user guides and supportable recovery accompany native-client/provider setup; no shared family code merges Nook into a sibling product.

  **Acceptance evidence:** Follow README installation, first-run setup and invited sign-in; upgrade existing data, run doctor, export/reset/recover and use the configured hosted owner claim. Check invalid config and missing secrets fail closed, operator disk custody is disclosed and no private value reaches logs/builds. Reproduce on a real supported owner account and inspect actual USD provider usage separately from local/fake-account tests. Install with an explicit data jurisdiction and confirm setup and doctor show it. Open the published reference cost for 50 and 300 members, check its unit costs and assumptions against the measured release figures, and compare the installation's actual cost per active member with it. Open the subprocessor list in `/docs` and doctor: it names the NOOK-078 Nook Push Gateway with its region, what it sees, availability objective, price and terms, and the reference estimate shows the gateway's price as its own line; switch to owner-credential mode and see the gateway leave the list and the developer account fees appear.

- **NOOK-051 — Auditable authority and actions.** Keep a durable, access-controlled audit of sign-in/session changes, invitations, roles, membership, app/agent grants, workflow runs, exports, holds, policy changes and destructive actions, including actor/service, time, scope, version, reason and outcome without private bodies or secrets. Audit cannot be edited by the subject; erasure anonymizes attribution without destroying count/time facts or policy-required preservation. Authorized admins filter/export the log, and automation links each proposed/performed action to its source, grant and human approval. Failed/uncertain actions remain distinct from confirmed effects.

  **Acceptance evidence:** Exercise membership revocation, export, erasure and an approved bot/workflow action, then filter/export their log through admin doors. Deny members and an erased actor; anonymize that actor and retain the action facts. Interrupt an external step and see uncertainty rather than a forged completion.

- **NOOK-052 — Accessible, responsive daily use.** Every daily, admin, migration, call and agent-control journey works with keyboard and screen reader, visible focus, named controls, contrast, reduced motion and error recovery at 1440 and 390 CSS pixels and supported native text scaling. Preserve routing/deep-link context and virtualized history focus; announce new activity without stealing focus, and keep captions/transcripts reachable. Target WCAG 2.2 AA with documented manual assistive-technology evidence; a bounded axe run is not full conformance. **Quick switcher and shortcuts:** ⌘K (Ctrl K on Windows/Linux and web) opens a quick switcher that jumps by fuzzy name to any conversation, person, DM, recent thread or public channel to browse and join, ranked by recency and unread, listing only what the member can currently read or join (a private channel they are not in never appears, even by exact name). Web and desktop ship a Slack-equivalent shortcut set, shown in a ⌘/ (Ctrl /) shortcut sheet: ⌘⇧A All unreads and ⌘⇧T Threads (NOOK-071), ⌘⇧K DMs, ⌥↑/↓ previous or next conversation, ⌥⇧↑/↓ previous or next unread conversation, ⌘[ and ⌘] back and forward, Esc marks the current conversation read, ⇧Esc marks all read (with undo), ↑ in an empty composer edits the member's last message, and ⌘F searches the current conversation (Ctrl and Alt replace ⌘ and ⌥ off macOS). Shortcuts never fire during IME composition and every one has a visible control equivalent.

  **Acceptance evidence:** At both widths and on native clients, use keyboard and VoiceOver/NVDA to send/reply/search, accept work, upload, join a captioned call, migrate, change a policy and pause an agent. Inspect realistic long names/history, enlarged text, focus after deletion/reconnect and error announcements; run scoped automated accessibility checks and record manual limitations. **Quick switcher and shortcuts:** keyboard only on macOS, Windows, Linux desktop and the web, ⌘K/Ctrl K then "inc" and Enter opens `#incidents`; a private channel the member is not in never appears, even by exact name; ⌘⇧A opens All unreads and ⌘⇧T Threads; Esc marks the current conversation read and ⇧Esc marks all read with undo; ↑ in an empty composer edits the last message; ⌘/ lists every shortcut; a screen reader announces each result.

- **NOOK-053 — Conversation notes and lightweight lists.** Members maintain channel/thread notes or canvas and lightweight checklists linked to discussion, with version history, contextual pins and current conversation permissions. An item may propose a commitment, but an assignee accepts separately under NOOK-004/010; a checked box never impersonates acceptance or verified completion. Link substantial documents/knowledge to Google Drive, OneDrive, Notion or Chest and project plans to Vector. Export, retention, hold and erasure cover notes without broadening the product into a document editor or tracker.

  **Acceptance evidence:** Create and revise a private channel note/checklist, link a provider document, propose an item to another person and require their exact acceptance. Remove a member, export history and delete/hold the note; all sources keep their distinct permissions and authority.

- **NOOK-060 — Organization policy and channel lifecycle.** Owners delegate explicit administrative permissions for people, channels, apps, security, billing/usage and compliance, with custom roles and user groups that never confer ordinary access to DMs. Manage channel name, topic, description, owner transfer, public/private policy, archive/restore and workspace navigation with history/audience disclosure before a privacy change; never widen private history silently. Support user-group mentions with visible recipients (channel-wide @channel/@here/@everyone are NOOK-065), guest expiry, invitation policy (who may invite members and guests, invitation links on or off with their maximum expiry and use limit, and invitation requests on or off) and approved email domains, each behaving as NOOK-018 defines, custom emoji with moderated upload, and org-wide defaults that members may override where permitted. Workspace settings enumerate who can create public and private channels, who can invite members and guests (and into which channels), the message edit and delete window (any time, never, or a set number of minutes, with audited admin exception), who can archive channels and who can use broadcast mentions (NOOK-065); default channels are auto-joined by every new member, or every member of a chosen group, at first sign-in. Owners set new-member notification defaults under NOOK-032: the push events that alert, a level for each default channel (for example `all new messages` on `#announcements`), and whether "Email me about mentions and DMs when I'm away" starts on; members may change their own. Bulk administrative actions preview affected scopes and retain audit; external shared-channel policy requires both orgs’ approval.

  **Acceptance evidence:** As owner create a delegated channel admin and a user group, rename/archive/restore a populated channel, transfer ownership and schedule guest expiry. Preview/refuse a private-to-public conversion that would expose earlier history, exercise group mentions and custom emoji, and deny app/compliance/DM operations outside the delegated grant. Verify expiry/revocation across phone, desktop, search and bot access. Restrict channel creation and guest invitation to admins and set a 15-minute edit window: a member's create, invite and late edit are refused through app and API, and a new member auto-joins both default channels. Turn invitation links off and approve one domain: creating a link is refused and the domain appears in the approved list. Set `#announcements` to `all new messages` and email on for new members: a member who joins afterwards starts with both, and an existing member's settings are unchanged.

- **NOOK-061 — Any office file, large uploads, previews and malware scanning.** Members attach any file type up to 1 GiB per file (Slack's bar) through resumable chunked uploads that survive network loss and app restart on every client. Common types preview in-app through NOOK-012's private, sandboxed, current-membership preview door: Word, Excel and PowerPoint (.docx/.xlsx/.pptx and legacy .doc/.xls/.ppt), PDF, CSV, plain text and code, images including HEIC/HEIF, animated GIF, WebP and SVG (rasterized, never executed), audio and video (MP4, MOV, WebM, MP3, M4A) with streamed playback and seek, and a .zip entry listing. Previews are rendered server-side into inert images, text or media segments; macros, scripts and external references never run or fetch. Every upload is malware-scanned (owned signature engine or a disclosed scanning subprocessor, signatures updated at least daily) before anyone else can download or preview it, and uploader and recipients see `scanning`, `clean`, `quarantined` or `scan failed`. A quarantined file has no download or preview path except an audited security-admin door, and the uploader sees why; a scanner outage holds files in `scanning` (or, by explicit owner policy, releases them labelled `not scanned`), never a silent clean. Owners and admins set allowed/blocked extensions and MIME classes, a lower size limit and storage policy; blocked types are refused before bytes are stored through every door: app, native share sheet, REST and Slack-compatible upload (NOOK-063), webhooks, workflows, forwarding, email-in and import. NOOK-012's private R2 custody, ordered binding, intent identity, deletion cascade and archive/erasure guarantees hold unchanged at this size, and NOOK-019 indexes extracted text from these types.

  **Acceptance evidence:** Upload and download a .docx, .xlsx, .pptx, .csv, .zip, HEIC photo, animated GIF and an MP4 screen recording within 5% of the 1 GiB limit at 1440 and 390 and on native clients; interrupt one upload mid-way and resume it with no duplicate; preview each type in-app and compare downloaded bytes by SHA-256. Upload the EICAR test file and see it quarantined for uploader and recipients with no download or preview path. Stop the scanner and see new files held as scanning. Block `.exe` as admin and see app, API and webhook uploads refused with no stored bytes; a file of 1 GiB plus one byte is refused.

- **NOOK-062 — Notification previews and actionable replies.** Each member chooses per account and per device how much native push (NOOK-021), Web Push (NOOK-015) and email (NOOK-014) show: `full` (sender, conversation and up to 160 characters of message text), `sender` (sender and conversation only) or `generic` ("New message in Nook"). **Defaults:** push is `full`, matching Slack's "Include a preview of the message"; email is `sender` until the member turns message text on. Owners set an org cap (force `sender` or `generic`), separately for DMs, private channels, shared/external channels and email; caps and choices are applied when each payload is built, so a change governs the next notification, and NOOK-040 labelled conversations are capped at `sender`. Text previews use the current revision under current ACL, never include deleted text, file contents or urgent reasons beyond the message, and stay encrypted end to end to the device (RFC 8291 for Web Push; a per-device key decrypted in the iOS Notification Service Extension and Android data handler for APNs/FCM), so neither the NOOK-078 push gateway nor APNs/FCM ever sees message text, sender or conversation names; the badge count rides inside the encrypted payload, and a device that cannot decrypt shows only the fixed `generic` wording. Native notifications offer **Reply** and **Mark as read** (iOS text-input action, Android RemoteInput, macOS/Windows/Linux desktop actions; Web Push offers Mark as read and Open where the browser supports actions). A reply posts through the ordinary message API with an intent key derived from the delivery ID and device, so a retried action posts exactly once, and it rechecks the device session, current membership, NOOK-066 posting permission and NOOK-069 second-factor state at send; a refused reply raises a visible failure notification instead of vanishing. Owners may require device unlock before a notification reply (required by default on phone lock screens). Mark as read advances the read frontier to the notified message and clears that notice and the badge on the member's other devices.

  **Acceptance evidence:** With previews at the default, mention a member whose iOS and Android devices are locked: each lock screen shows sender, conversation and message text. Turn on the org cap forcing `sender` and send again: the same event shows sender and conversation only, and the delivered payload contains no text. Turn on email text and see it in the received email; turn it off and see the next email without text. Reply from the lock-screen notification on both phones and from a desktop notification, drop the network once and retry: exactly one message posts under current membership. Remove the member from the channel and reply from a stale notification: the reply is refused with a visible failure. Mark as read from a notification and see unread, badge and the other device's notice clear. Capture the packets for a `full` notification at the NOOK-078 gateway: no message text, sender or conversation name appears.

- **NOOK-063 — Slack-compatible webhooks and Web API.** Existing Slack senders and Slack SDK apps move to Nook by changing only the URL, or the API base URL and token. **Incoming webhooks:** each webhook URL (`/hooks/slack/<opaque>`) posts to one chosen conversation as a named app identity, accepts Slack's JSON or form `payload=` body with `text`, `blocks`, legacy `attachments` (mapped to the subset below), `thread_ts`, `mrkdwn`, `unfurl_links` and `username`/`icon_emoji`/`icon_url` (shown as that app's display, never as a person), and answers with Slack's `ok` or error strings (`invalid_payload`, `channel_not_found`, `no_text`, `action_prohibited`, `rate_limited`). **Web API** at `/api/slack/<method>` with bot or user tokens in `Authorization: Bearer`, Slack's `{ok, error, response_metadata.warnings}` envelope and `429` with `Retry-After`: `auth.test`, `chat.postMessage`, `chat.update`, `chat.delete`, `chat.postEphemeral`, `chat.getPermalink`, `chat.unfurl`, `reactions.add`/`remove`, `conversations.list`/`info`/`history`/`replies`/`join`/`members`, `users.conversations`, `users.info`/`list`/`lookupByEmail`, `users.profile.get` (only fields visible to the app under NOOK-017 field visibility), `views.open`/`update`/`push`/`publish`, the `files.getUploadURLExternal`/`files.completeUploadExternal` upload pair, `chat.scheduleMessage` with `chat.scheduledMessages.list` and `chat.deleteScheduledMessage` (a NOOK-034 scheduled message, fenced again at firing time), `conversations.open` (opens or resumes only that app's NOOK-025 app DM with one person, or with several people a group DM that includes the app, each person having app DMs unblocked; it never opens or reads a person-to-person DM) and the channel-management calls `conversations.create`, `conversations.invite`, `conversations.kick`, `conversations.setTopic`, `conversations.setPurpose`, `conversations.rename` and `conversations.archive` (only under the app's NOOK-025 channel-management scope, each use audited). `users.conversations` and `conversations.members` return only conversations the app is a member of. A missing scope returns Slack's `missing_scope` with `needed`/`provided`, a person who blocked the app returns `messages_tab_disabled`, and a conversation outside the token's grant returns `channel_not_found`, never its existence. Slack-style IDs (`C…`, `U…`, `ts`) map stably to Nook conversation, actor and message IDs, and imported Slack IDs (NOOK-041) resolve to their imported records. **Events API:** signed delivery (`X-Slack-Signature` v0 HMAC-SHA256 with `X-Slack-Request-Timestamp`) to the app's request URL with `url_verification`, `event_callback` envelopes, `event_id` deduplication, Slack's retry headers and these events: `message` (including `message_changed`, `message_deleted`, thread replies and `channel_join`), `app_mention`, `reaction_added`/`reaction_removed`, `member_joined_channel`/`member_left_channel` and `link_shared`. **Slash commands and interactivity:** signed form-encoded command requests (`command`, `text`, `user_id`, `channel_id`, `response_url`, `trigger_id`), immediate `ephemeral` or `in_channel` responses within 3 s, up to five `response_url` follow-ups within 30 minutes, `block_actions` button/select payloads and `view_submission` modals. **Block Kit subset:** `section` (text and fields), `header`, `divider`, `context`, `image`, `actions` (buttons and static selects), `rich_text`, and mrkdwn bold, italic, strike, code, preformatted, quotes, lists, `<url|label>`, `<@U…>`, `<#C…>` and `<!here>`/`<!channel>`/`<!everyone>` under NOOK-065/066 permission. An unsupported block, element or field renders the payload's text fallback with a visible "Part of this message can't be shown in Nook" notice and is reported in `response_metadata.warnings`; nothing is dropped silently. A compatibility matrix in `/docs` lists every method, event, payload field and block as supported, degraded or unsupported and is checked against the implementation. Every call runs under NOOK-025's scoped grants, current membership, NOOK-066 posting permission and NOOK-061 file policy; tokens are revocable and never reach a DM not explicitly granted to the app.

  **Acceptance evidence:** Repoint an unmodified GitHub Actions `slackapi/slack-github-action` incoming-webhook step and a curl `{"text","blocks","thread_ts"}` payload to a Nook webhook URL: both render with their blocks and the threaded one lands in its thread. Run an unmodified `@slack/bolt` JavaScript app and a Python `slack_sdk` script with only base URL and token changed: `chat.postMessage` then `chat.update` edits one message; signed `message` and `reaction_added` events arrive once across a forced retry; a slash command answers ephemerally and through `response_url`; a button click returns `block_actions`. The same Bolt app calls `conversations.open` for a user and `chat.postMessage` into the result: that person receives one app DM showing the app identity. It creates `#inc-123`, invites three responders, sets its topic and lists them with `conversations.members`; schedules a message with `chat.scheduleMessage` that posts once at its time; and advances a modal with `views.push` and `views.update`. The person blocks the app and the next DM is refused with `messages_tab_disabled` and posts nothing; a second app without the channel-management scope gets `missing_scope` from `conversations.create`; and no other DM or group DM is readable through either token by `conversations.history`, `users.conversations` or events. Post an unsupported block and see the text fallback, visible notice and warning. Forge a signature, replay an old timestamp, post to an owners-only channel without permission and revoke the token: each is refused with Slack's error string and no message.

- **NOOK-064 — Zapier, Make and n8n connectors.** Nook publishes and maintains a public Zapier App Directory integration, a Make app and an n8n node over NOOK-025's OAuth 2.0 scoped grants. **Triggers:** new message in a chosen channel (optionally matching text), new mention of me, new direct message, reaction added (optionally a chosen emoji), new channel, new saved item, commitment accepted or completion reported, and approval decided. **Actions:** send channel message (with thread and blocks), send direct message, reply in thread, add reaction, set a reminder for a person (NOOK-033), schedule a message (NOOK-034), propose a commitment (it stays proposed until its assignee accepts) and request an approval (an interactive approve/decline message whose decision fires the approval trigger). **Searches:** find person by email, find channel, find message. Triggers use REST hooks with deduplicated polling fallback; every run appears in the app's delivery log with failures and uncertainty; posting permission (NOOK-066), file policy and current membership apply to every action, and revoking the grant stops new triggers and actions.

  **Acceptance evidence:** In a real Zapier account, run zaps end to end: a Nook "new message in #sales" trigger appends a Google Sheets row, and a Google Forms submission runs Nook "send channel message" and "request approval"; decide the approval and see the decision trigger fire once. Repeat one trigger-plus-action scenario in Make and one workflow in n8n. A "propose commitment" action stays proposed until the assignee accepts. Revoke the grant and see the next run fail visibly with no post.

- **NOOK-065 — Channel-wide mentions: @channel, @here and @everyone.** Members send `@channel` (every current member of the conversation except the sender), `@here` (current members with fresh active presence on any client: a foreground heartbeat within the last 10 minutes and status not set to away) and `@everyone` (every active non-guest member of the organization, admitted only in the designated organization-wide default channel and refused elsewhere with a hint to use @channel). Recipients are resolved once at commit against current membership and presence; identical retries never re-resolve, and broadcasts are not bounded by NOOK-013's 50 explicit individual mentions. A broadcast creates a `broadcast` Attention reason below explicit mention precedence, follows each recipient's push/email `broadcasts` choice (on wherever mentions are on, by default), is suppressed in muted conversations and by a per-conversation "Ignore @channel, @here and @everyone" choice, and is deferred by quiet time like routine items (it never uses urgent bypass). When a broadcast would reach six or more people, the composer confirms with the recipient count and how many are in other time zones or quiet time; the API requires `confirmBroadcast:true` or returns 409 with the count and commits nothing. Owners set who may broadcast (everyone, owners and admins, or named people and groups) with per-channel overrides under NOOK-066; a refused broadcast commits no message, and the same policy binds apps, Slack-format `<!channel>`/`<!here>`/`<!everyone>` webhooks, workflows and scheduled messages at firing time.

  **Acceptance evidence:** In a 120-member channel with mixed choices, @channel creates Attention, push and email for every eligible member per their choices (none for the sender, muted or ignore-broadcast members; deferred for members in quiet time) and the delivered count equals 119 minus those exclusions. @here with 30 members in fresh presence reaches exactly those 30. @everyone in the default channel reaches every active non-guest and is refused in another channel. Send through the API without confirmation and receive 409 with no message; confirm and see one send. A member outside an owners-only broadcast policy, and a webhook `<!channel>` from an app without that permission, are refused with no message.

- **NOOK-066 — Channel posting permissions and announcement channels.** Channel managers and admins set per-channel permissions: who may post top-level messages (everyone, owners and admins, channel managers, or named people and user groups), who may reply in threads (everyone or the same list), who may use broadcast mentions (NOOK-065) and which apps, webhooks and workflows may post. Members who cannot post still read, react, save, follow, open threads (and reply where replies are open) and forward under NOOK-067; their composer shows the restriction instead of a send box. Everyone may post by default. The permission is fenced in the same D1 batch as membership on every write door: app, native client, notification reply (NOOK-062), REST and Slack-compatible API and webhooks (NOOK-063), Zapier/Make/n8n (NOOK-064), workflows (NOOK-026), scheduled messages and reminders at firing time (NOOK-033/034), forwarding and email-in (NOOK-067) and agents (NOOK-054); a refused write commits nothing. Admin override posts are labelled and audited (NOOK-051), and every permission change is audited and shown in channel details.

  **Acceptance evidence:** Set #announcements to owners-only posting with replies off. A member's post, thread reply, notification reply, Slack-compatible webhook post, Zapier action, workflow post and a scheduled message created before the change are all refused (the scheduled one as a visible failed occurrence), while the member still reads, reacts and saves. An owner posts; an admin override post is labelled and appears in the audit log. Open replies and see the member's thread reply accepted.

- **NOOK-067 — Forwarding messages and email into conversations.** From any message on every client, a member forwards to one or more channels, DMs, group DMs or a thread, with an optional note and optional scheduled time (NOOK-034). The forward shows original author, original conversation (named only to recipients who can read it), time and a source link that opens only under the recipient's current membership. Public-channel sources forward with their body. From a private channel, DM or shared channel the sharer chooses **Show message** (an authorized, previewed and audited disclosure of that message's body and attachments to the destination audience) or **Link only** (attribution and link; a recipient without source access sees no body, attachment, excerpt, reactions or thread). Owners can restrict forwarding from private and DM sources to link-only or block it, and NOOK-040 labels apply. Forwarding needs posting permission in each destination (NOOK-066), and forwarding into an external shared channel needs both organizations' policies (NOOK-031). Source edits update the forwarded snapshot; source deletion or erasure tombstones every forwarded copy by default, and a NOOK-038 hold preserves only the custodian copy. Channels can also have a revocable inbound email address: forwarded email posts as an attributed "Email" message with sender, subject, sanitized text and attachments (scanned under NOOK-061), with optional sender allow-listing and spam limits, and never creates accepted work.

  **Acceptance evidence:** Forward a public-channel message with a note into a DM on desktop and phone and see author, source and note. Forward a private-channel message to a public channel as Link only: a public member without private access sees attribution but no body, attachment or thread, and the link refuses them; forward it as Show message and see the disclosure preview and audit entry. Set owner policy to link-only and see Show message refused. Delete the source and see each forwarded copy tombstoned. Email a PDF to a channel's address and see one attributed post; with allow-listing on, an unlisted sender is refused.

- **NOOK-068 — Safe link previews and app unfurls.** Links pasted by people get a title, description, site name and image preview from a server-side unfurl service that fetches only http/https on ports 80 and 443; resolves DNS itself and connects only to the checked public unicast address, refusing loopback, RFC 1918, 100.64.0.0/10, link-local 169.254.0.0/16 (including cloud metadata), multicast, IPv6 loopback/ULA/link-local and IPv4-mapped forms, rechecked on every redirect (at most three) to defeat DNS rebinding; sends no cookies, credentials or Nook identity; stops at 5 s, 1 MiB of HTML and 5 MiB per image; parses Open Graph, Twitter cards and oEmbed for allow-listed providers such as YouTube, Vimeo, Loom and Figma; proxies and re-encodes preview images through Nook so viewers never contact the target; and caches each URL for at most 24 hours. Previews render inertly, with embedded players only from the oEmbed allow-list. Authors remove a preview from their message (removal persists), members hide previews for themselves or turn them off, and admins allow or deny domains, disable previews in private channels, DMs or shared channels and restrict third-party app unfurls. An app with a claimed domain receives `link_shared` (NOOK-025/063) and supplies blocks through `chat.unfurl`; the first time it would unfurl a member's link in a conversation, that member approves it. Connector cards (NOOK-029) use their own grants and are never fetched anonymously.

  **Acceptance evidence:** Paste a public article URL and see title, description and image preview with the image served from Nook; paste a YouTube and a Loom link and see their oEmbed previews. Paste links resolving to 127.0.0.1, 10.0.0.5 and 169.254.169.254 and a public URL that redirects to an internal address, and confirm through the fetch log and a listening internal server that nothing internal is contacted. An installed app with a claimed domain unfurls its own link once the author approves. The author removes a preview and it stays removed after reload on another device; an admin denies a domain and its links get no preview.

- **NOOK-069 — Two-factor authentication without SSO.** Owners require two-factor authentication for all members and guests, or for chosen roles first, without an identity provider. Second factors are TOTP authenticator apps (RFC 6238, 6 digits, 30 s, one-step skew) and WebAuthn passkeys or security keys; a user-verifying passkey may also be the sign-in method and satisfies the requirement. A magic link alone yields a pending session that can only complete the second factor or enrolment: every private read and write, SSR page, live socket, export and API call returns `second_factor_required` until it is satisfied, so access to the sign-in inbox alone never opens a session. When enforcement starts, every member without a factor is sent to enrolment at their next request and existing sessions lose private access until they enrol, with an enrolment-progress view for owners. Ten single-use recovery codes are issued at enrolment, stored hashed and regenerable; each use is audited and emailed to the member. An owner or admin with a fresh second factor may reset another member's factors: the reset revokes their sessions and devices, is audited (NOOK-051) and notifies the member; the last owner recovers only with a recovery code or the documented offline operator recovery. Native clients support TOTP entry and platform passkeys. NOOK-074's email codes, universal links, QR sign-in from desktop and personal providers each yield the same pending session, so none of them bypasses enrolment or a required second factor. Failed attempts are rate-limited with audited lockout, and export, erasure, policy and second-factor reset require a recent second factor. App and bot tokens are scoped credentials outside the human factor. With SSO (NOOK-037) the IdP's MFA applies instead. Every member, with or without SSO, sees their own active sessions and devices (client, OS, sign-in method, approximate location and last activity) and can sign out any one or all others; signing out a native device also revokes its push registration and purges its offline cache at its next contact (NOOK-021).

  **Acceptance evidence:** As owner, enforce 2FA. A member without a factor signs in by magic link and reaches only enrolment: every private API, SSR page, live socket and export is refused until they enrol with TOTP; a second member enrols a passkey on desktop and phone. A captured magic link used from another browser opens no private access without the second factor. Use a recovery code and see it audited and emailed; an admin resets a member's factors and the audit log, revoked sessions and notice show it; repeated wrong TOTP codes lock out with audit. The member opens their own sessions list, sees a phone and two browsers, signs out the phone and then all others: the phone loses private access and push at its next contact and the other browsers return to sign-in.

- **NOOK-070 — Polls, GIFs, thread broadcast, bookmarks and translation.** Members create polls in any conversation they may post to (single or multiple choice, up to 20 options, named or anonymous votes, optional close time) with live counts visible to current members and results kept in export; a vote is never a decision or accepted commitment. A GIF picker searches GIPHY or Tenor through a Nook proxy that withholds viewer identity, with an admin on/off switch and content rating (G/PG by default); chosen GIFs post as images under NOOK-061 policy. A thread reply can also be sent to the channel ("Also send to #channel"), appearing in both places as one message with one edit/delete and its thread link. Channels carry a bookmarks bar of links, files and folders that permitted members add, reorder and remove, with audit and ACL-checked previews. On demand a member translates a message into their chosen language with the original one tap away; translation follows the organization's AI policy (NOOK-040), keeps no copy beyond the message's retention and never edits the source.

  **Acceptance evidence:** Create a named and an anonymous poll, vote from desktop and phone, close it and see the final count; a non-member cannot vote. Post a GIF through search under the rating cap, then disable GIFs as admin and see the picker gone. Send a thread reply also to the channel, edit then delete it and see both places update once. Add, reorder and remove channel bookmarks as a permitted member and be refused as a restricted one. Translate a Spanish message into English and return to the original.

- **NOOK-071 — Threads view and group-DM conversion.** A sidebar Threads view lists every thread the member follows (explicitly under NOOK-013, or automatically under NOOK-072 because they authored it, replied in it, were mentioned in it or it is in a one-to-one DM, plus every thread in a conversation they set to all thread replies) across conversations they can currently read, ordered by newest unread reply, with unread reply counts, inline reply and mark-read; threads in a muted conversation appear only when explicitly followed, and losing membership removes a thread from the view at the next request. Reads in the view advance the same per-thread read state as the conversation (NOOK-005) on every device. An **All unreads** view (a sidebar entry the member may hide, and ⌘⇧A / Ctrl Shift A) lists every conversation with unread messages in sidebar order, each with its unread messages in order and the member's current ACL applied, with mark-read per conversation and Mark all read with a ten-second undo; its reads advance the same NOOK-005 frontier on every device and never close Attention or accept work. Any participant with current authorized access can convert a group DM into a private channel, named at conversion, preserving its full history and retaining the complete fixed original participant roster as read-only history. Only participants who still hold authorized access at conversion time become channel members; anyone who left or lost access stays out until an explicit regrant under NOOK-018, and conversion never restores their private-history access. The group DM stays fixed under NOOK-003, becomes read-only and links to the channel. The conversion is shown in both conversations and audited (NOOK-051); it never widens the audience, and later invitations and regrants follow ordinary channel membership.

  **Acceptance evidence:** Follow three threads across two channels and a DM, receive replies and see them in the Threads view with unread counts at 1440 and 390; reply inline, mark one read and see it read on a second device; lose membership of one channel and see its thread leave the view. Convert a three-person group DM into private channel `#proj-x`: with all three still authorized, its members are those three, its full history is present and its complete original roster is retained as read-only history, the group DM is read-only with a link, an outsider cannot discover either, and the audit log shows the conversion. Repeat after one participant, including the original creator, leaves or loses access: the complete original roster remains as read-only history, but only the two currently authorized participants become members, and the converter manages the channel. Reactivate the removed participant: they still cannot read the channel or private history until an explicit channel regrant under NOOK-018. Export and restore before and after that regrant, restart, and verify the same roster, membership and access boundaries. Threads the member started, replied in or was mentioned in appear without an explicit follow (NOOK-072), and an unfollowed one leaves the view. **All unreads:** with unread messages in three conversations, ⌘⇧A lists them in sidebar order; marking one read removes it and clears it on a second device; Mark all read clears the rest and undo within ten seconds restores them; no Attention item closes.

- **NOOK-072 — Automatic thread following and channel-wide thread replies.** In a channel, private channel, shared channel (NOOK-031) or group DM, a member automatically follows a root thread when they author its root, post a reply in it, or are explicitly mentioned or broadcast-mentioned (NOOK-065) in its root or a reply; in a one-to-one DM both participants follow every thread by default. The follow is written atomically with the committing message and is the same followed state as NOOK-013's explicit follow: it appears among the followed root-thread IDs from `GET /api/conversations/:id/preferences`, each with its `source` (`explicit`, `authored`, `replied`, `mentioned`, `dm` or `all`), and drives the `thread-follow` Attention reason, the "replies in followed threads" alerts of NOOK-015, NOOK-021 and NOOK-032, unread counts and badges, and the NOOK-071 Threads view. A member's own replies never alert them, and imported history (NOOK-041–046) never creates follows of any kind; a native reply in an imported thread follows as usual. Any member can unfollow any thread from its menu, the Threads view, a notification action or `POST /api/messages/:id/follow {follow:false}`. An unfollow sticks: a later mention of that member alerts as a mention but does not re-follow, while a later reply by that member re-follows. In a muted conversation, NOOK-013's mute still suppresses routine follow alerts, and losing membership leaves the follow dormant: it never routes or reveals the thread until membership returns. **All thread replies:** per channel, private channel, shared channel or group DM, a member may set `threads` to `all` (default `followed`), with an optional different `mobileThreads`, through `GET/PATCH /api/conversations/:id/notify` and every client. Every thread in that conversation then counts as followed for that member, every reply alerts and appears in their Threads view, and a per-thread unfollow still wins. `GET /api/conversations/:id/preferences` returns both `threads` (root IDs) and `threadFollows` (objects with `message` and `source`): live roots without a stored follow return virtual source `all` when either `threads` or `mobileThreads` is `all`, with per-thread unfollows excluded. A stored follow keeps its original source. `GET /api/threads` likewise returns source `all` for those virtual entries under current membership, import-publication and mute rules; no stored follow is created by this view. It follows NOOK-032's mobile override, quiet schedule, exception list and `nothing` level (which keeps those replies silent in the Threads view), NOOK-062 previews and the NOOK-014/NOOK-015 gathering and ten-minute folding rules. **Thread badge choice:** followed-thread replies count toward unread and every device's badge by default; a member may choose "Count thread replies only when they mention me", applied identically to the native (NOOK-021) and Web Push (NOOK-015) badges, and those replies still appear in the Threads view.

  **Acceptance evidence:** With every member signed in on a phone and a desktop app and not active on desktop (NOOK-073), in a `#sales` channel at the default `mentions` level, A posts a root message and B replies in its thread without mentioning A: A's phone and desktop each alert once, the thread shows in A's Threads view with one unread reply, and A's preferences list it with source `authored`. C replies once; D's later reply alerts both A and C. E is @-mentioned in the thread, then F replies, and E is alerted for F's reply. C unfollows: the next reply does not alert C; G then @-mentions C in the thread and C receives only that mention alert, and the following plain reply does not alert C; C replies again and the next reply alerts C. In a one-to-one DM, a reply in any thread alerts the other participant. With `#incidents` set to all thread replies, a reply in a thread the member never touched alerts them and appears in their Threads view, and the NOOK-032 exception list shows `#incidents`. Request `GET /api/conversations/:id/preferences` and `GET /api/threads`: the untouched live root is in `threads`, its `threadFollows` entry has `message` equal to that root ID and `source: "all"`, and its Threads item also has `source: "all"`; existing stored follows keep their sources. Repeat with only `mobileThreads: "all"`, then unfollow the untouched root and verify both endpoints exclude it. A member removed from `#sales` gets no alert and no Threads-view row for its threads. Importing a Slack history in which the member authored threads creates no follows and no alerts; their first native reply in one of those threads follows it. With the mentions-only thread badge chosen, a non-mentioning followed reply leaves the phone and desktop badges unchanged and still appears in the Threads view.

- **NOOK-073 — Active-elsewhere device routing.** A member is *active on desktop* while any of their desktop apps (NOOK-021) or web clients has had user input (keyboard, pointer or window focus) within the last 10 minutes and its OS session is not locked; locking the screen, or the OS reporting the session locked or asleep, ends that client's activity 1 minute after the lock. Activity comes from the same signed, session-bound heartbeat on every client (`POST /api/devices/presence {id, page, visible, lastInputAt, locked}`, renewed every 15 seconds while visible), never from an open socket alone; a heartbeat from a revoked session, a removed device or an inactive account does not count, and a client whose heartbeats stop (crash, sleep, network loss) stops counting when its last heartbeat expires after 45 seconds, so a lost laptop never holds alerts for 10 minutes. While a member is active on desktop, eligible alerts go to their desktop and web clients only, and phone and tablet alerts for those events are held. A held mobile alert is released when the member becomes inactive, after their chosen delay (immediately by default, or 1, 2, 5, 10, 15 or 30 minutes after becoming inactive), folded with other held items under the NOOK-014/NOOK-015 ten-minute conversation folding rule, and cancelled if the event is read or done on any device first (NOOK-005, NOOK-014, NOOK-015). A member may instead choose "Always send to mobile, even when I'm active", per account or per device, through `GET/PATCH /api/notify {mobileWhenActive: "inactive"|"always", mobileDelayMinutes}` and every client. Call invites (NOOK-022) and urgent requests the member lets bypass quiet time (NOOK-032) ring or alert every device regardless of activity. The member's device delivery view shows, per device, its activity, last input, lock state and current routing (for example "Phone held: active on MacBook"), and lists each held, released and cancelled alert with its reason. NOOK-021's delivery bar is measured against this routed device set: a held mobile alert cancelled by a read is recorded as `held-active-elsewhere`, neither displayed nor missed nor silently dropped, and a released one is timed from its release.

  **Acceptance evidence:** A member uses the macOS app with the default setting and their phone unlocked on the desk. A DM and a mention alert on the desktop only, and the phone stays silent; the device delivery view shows both held for the phone. The member reads the DM on the desktop, then locks the screen: about 1 minute later only the unread mention alerts on the phone, exactly once, and the DM is recorded as `held-active-elsewhere`. With the desktop idle for 10 minutes and unlocked, a new DM alerts the phone. Force-quit the desktop app while active: the phone alerts for a new DM within 45 seconds plus the delivery bar. Switching to "Always send to mobile, even when I'm active" makes the next DM alert both devices. With a 5-minute delay chosen, a mention after locking alerts the phone 6 minutes after the lock (1-minute lock threshold plus the delay). A call invite rings the phone while the member is active on desktop. Repeat with a focused web client in Chrome in place of the desktop app, and on Windows and Linux. A heartbeat replayed from a revoked session holds nothing. In the NOOK-021 500-event delivery-bar run with desktops active for part of the window, held events appear as `held-active-elsewhere` or are timed from release, and none is counted as displayed or silently missing.

- **NOOK-074 — Native sign-in and finding the installation.** Each Nook installation is its own service, so every native client first learns which installation it belongs to and then signs in the device that asked; no path signs in a different browser or device than the requester, and none falls back to another installation. **Finding the installation:** every invitation, join confirmation (NOOK-018) and cutover invitation (NOOK-041, NOOK-076) email carries an open-in-app link and a QR code that both encode the installation's address. On a phone with the app installed they open the app already pointed at the installation; without the app they open its App Store or Play Store listing, and the first launch after install opens with that installation preselected (the store referrer on Android, the verified link reopened from the same email or QR on iOS), never chosen by guessing. The app and the desktop apps also accept a typed installation address. Every address, however it arrives, is checked over HTTPS against the installation's published descriptor at `/.well-known/nook-installation.json` (installation ID, display name, icon, enabled sign-in methods and minimum client version), signed by the installation's key, and that key is pinned on first connection. An invalid certificate, a missing, unsigned or mismatched descriptor, an unreachable host or a site that is not a Nook installation shows an explicit error naming the address and connects nowhere; a later key change for an installation already added asks the member to confirm it as if it were new. **Managed deployment:** signed MSI and MSIX packages for Windows (Intune, Group Policy), a signed PKG for macOS (Jamf, Kandji, Intune), .deb and .rpm packages for Linux, and iOS and Android managed app configuration (AppConfig keys through any MDM) let IT preset the installation address, lock the app to it, disable adding other workspaces and choose the update channel. A preset address is still verified against the descriptor. **Email code on any device:** every sign-in email carries a one-time 6-character code beside the magic link. The code belongs to the pending sign-in that one device started, so typing it into that app or browser signs in that device only. It is single-use, valid for 10 minutes, refused on any other device or browser, invalidated when its link is used or a newer code is requested, stored hashed, and rate-limited per pending sign-in, address and client IP, with an audited lockout (NOOK-051) after 5 wrong entries. **Universal links and app links:** sign-in, invitation and permalink URLs are served from the installation's own domain with `apple-app-site-association` and `assetlinks.json` (or the published descriptor, for installations whose domain the app does not yet know), so that on iOS and Android with the app installed a tapped sign-in link opens the Nook app as a verified universal link or Android App Link and completes the app's own pending sign-in, creating no browser session. A link opened on a device other than the one that requested it shows that request's device and the 6-character code to type there, and signs in the current browser only if the member explicitly chooses that instead. **Sign in on mobile from desktop:** a signed-in desktop or web client offers "Sign in on mobile", showing a single-use QR code valid for 60 seconds. The phone app scans it, the desktop shows "Sign in iPhone (Jane's)?" with the phone's device name, OS and approximate location, and only after that confirmation does the phone receive its own new session with its own token, never a copy of the desktop session. That session still meets NOOK-069 second-factor policy, is listed in the NOOK-069 sessions list with method `qr-from-desktop`, and is audited. A rescanned, expired, declined or other-installation code is refused, and owners can switch this method off. **Personal sign-in providers:** owners may enable Continue with Google, Microsoft or Apple without configuring organization SSO. The provider's verified email must exactly match an existing member's confirmed mailbox, a pending invitation's address, or an approved domain under NOOK-018, so it is one more way to confirm a mailbox and never a fifth join door; an Apple private-relay address matches only an address already recorded for that member. **SSO and passkeys in native apps:** under NOOK-037 enforced SSO, native apps sign in through the IdP in the system authentication browser (ASWebAuthenticationSession on iOS and macOS, Custom Tabs on Android, the default browser with a claimed HTTPS or loopback redirect on Windows and Linux) using PKCE (S256) and state, never an embedded web view. While SSO is enforced, email codes, magic links and personal providers are disabled apart from NOOK-037's audited recovery route, and QR sign-in stays available only when the owner allows it, with the phone session bound to the IdP session policy. Enrolled members can sign in with a platform passkey (NOOK-069) on every native client. **Second factor and lifecycle:** every path above yields at most a NOOK-069 pending session when a second factor is required; deactivation (NOOK-018), session sign-out (NOOK-069) and SCIM offboarding (NOOK-037) end native sessions the same way. After first sign-in the device continues to NOOK-021's notification permission step.

  **Acceptance evidence:** A freshly invited member opens the invitation on a laptop and scans its QR code with an iPhone that does not have the app: the App Store listing opens, and after install the app opens with the installation preselected. The member requests sign-in from the phone; the email arrives on the laptop, and typing its code into the phone signs the phone in. Reusing the code, using it after 10 minutes or typing it into a third device is refused, and 5 wrong codes lock out with an audit entry. Clicking that email's link on the laptop instead shows that the iPhone requested it, with the code, and signs the laptop browser in only after the member chooses that explicitly. On an Android phone with the app installed, tapping the magic link in Gmail opens the Nook app signed in, and no browser session is created. A member signed in on the macOS app chooses "Sign in on mobile"; the Android app scans the QR code and the desktop confirms the named phone. The phone is signed in with its own session, listed in NOOK-069 sessions as `qr-from-desktop`, and signing out the desktop leaves the phone signed in. Rescanning the same code, or scanning it after 60 seconds, is refused. With 2FA enforced, each of these paths reaches only second-factor entry or enrolment. With SSO enforced, the iOS app signs in through Okta in the system browser with PKCE, and the email-code and magic-link options are absent. Typing a mistyped installation address, or an address whose descriptor signature does not match, shows an error naming the address and connects nowhere. A Windows laptop receiving the MSI through Intune and an iPhone receiving AppConfig through the MDM both open locked to the preset installation. With Continue with Google enabled, a member whose Google address matches their confirmed mailbox signs in on the iPhone, and an unknown Google address is refused with no account created.

- **NOOK-075 — External participants without a Nook installation.** A shared channel or external DM (NOOK-031) can invite by email a person whose organization runs no Nook installation, the way Slack invites people outside Slack to try Slack Connect. That person joins our installation as an *external participant*: they confirm their own mailbox through a NOOK-074 path (email code, link, passkey or personal provider; never an invitation link or approved-domain join), and every client shows them with the external indicator and the organization name the inviter entered, marked unverified unless their mailbox domain matches it. They can open only the channels and DMs they were invited to. They are never in our directory, people search, channel browser, default channels or user groups; they receive `@channel`/`@here` (NOOK-065) only in the conversations they share, can start DMs only with members who share a conversation with them and only where our NOOK-031 policy allows external DMs, and cannot create channels, install apps or invite anyone. Our organization's NOOK-031 policy governs them exactly as for a Nook-to-Nook shared channel: retention, export, media and calls, apps, NOOK-061 file policy and NOOK-040 labels and DLP, with our retention banner shown to them. An owner policy, shown before anyone is invited, states which roles may invite external participants, whether each invitation needs admin approval, an invitation expiry (default 14 days), whether NOOK-069 second factors are required of them, and whether such people are created as external participants (the default) or as NOOK-018 single-channel guests. External participants use the web and native clients (NOOK-021, NOOK-074), with their own notification choices (NOOK-032). Removing one from a conversation, or disconnecting their organization in one action that covers every participant carrying that organization name, ends access on every client, search, push and API at the next request, and the audit (NOOK-051) records who invited, approved, removed and disconnected. Their content follows our retention, holds and ERASURE-001. If that organization later runs its own Nook, a mutually approved NOOK-031 connection takes over the conversation: each external participant confirms the same mailbox on their home installation, their authorship and the conversation's history and audience carry over unchanged, and their external-participant account on our installation is retired.

  **Acceptance evidence:** In our installation, invite `ana@acme.example`, whose organization runs no Nook, to the shared channel `#acme-project` and to an external DM. She confirms her mailbox with a code in the iOS app and sees only `#acme-project` and that DM, with our retention banner; our members see her with the external badge and "Acme (unverified)". She cannot browse channels, find anyone in the directory or people search, open `#vendor-print`, a default channel or any other DM, start a DM with a member she shares nothing with, create a channel or invite anyone, and each attempt is refused by the API as well as hidden in the client. `@channel` in `#acme-project` reaches her; `@here` in `#general` does not. Sharing a file labelled Confidential (NOOK-040) into `#acme-project` is blocked by DLP. She posts, and our members get the reply under their ordinary NOOK-032 levels on phone and desktop. With the policy set to require admin approval, a member's invitation waits for an admin; with it set to guests, the next invitation creates a single-channel guest instead. Remove Ana: her web, iOS and API sessions lose `#acme-project` and the DM at their next request and push stops. Invite two more Acme people, then disconnect Acme: all of them lose access on every client and API, and the audit shows each step. Later Acme runs Nook: after a mutually approved NOOK-031 connection, Ana confirms the same mailbox there and keeps the channel's full history, her authorship and the same audience.

- **NOOK-076 — Slack Connect cutover.** Cutover from Slack (NOOK-041) never leaves a Slack Connect conversation silently behind. **Preview:** the cutover preview lists every Slack Connect channel and external DM found in the export (shared channels and messages whose `user_team` or `source_team` differs from the exporting workspace, with embedded `user_profile` data), the owner's approved source grant (`conversations.list` with `is_ext_shared`, `conversations.members`, `users.info`, `team.info`) and each consenting member's grant for private Connect channels and external DMs. Each row shows the channel or DM, every external organization (Slack team ID and name), its participants (Slack user ID, name, and email where Slack supplies it, otherwise marked as missing) and the last-activity date and imported message count. **Disposition per external organization and conversation:** the owner chooses (a) invite those people as NOOK-075 external participants in one reviewed action, with an editable cutover message and the NOOK-041 cutover time; a participant without a Slack-supplied email stays pending until the owner enters an address, which is recorded as owner-entered and is never guessed; (b) invite a partner organization that runs Nook to a NOOK-031 shared channel by its installation address (NOOK-074 descriptor), the imported channel becoming that shared channel once both sides approve; or (c) leave it in Slack with a recorded reason. Optionally, through the owner's grant with `chat:write`, Nook posts the cutover message into the Slack Connect channel so the partner sees where the conversation moved. **History and identity:** Slack Connect history imports under NOOK-041's rules, with external authors as imported identities carrying their organization and the external indicator. Accepting an invitation claims that person's imported authorship through the Slack-user-ID mapping recorded on the invitation, never by display name or email collision. An external participant is granted only the channels and DMs that were shared with their organization in Slack, with each channel's imported history as their organization could read it there (from their organization's first recorded join where the source records it); no external participant ever gains a channel, DM, message or file that was not shared with their organization in Slack. **Report:** the NOOK-041 cutover report shows, per external organization and per conversation, invited, joined, pending, bounced and left-in-Slack counts with reasons, resends pending invitations in one action, and keeps the cutover checklist open with "external conversations unresolved" until every Slack Connect conversation has a disposition; no external channel is silently dropped. **Catch-up:** NOOK-077 delta passes include Slack Connect conversations; their new messages, edits and deletions import under NOOK-077, and a Slack Connect channel, external DM or external participant first found by a later pass enters this preview and needs a disposition before the checklist closes.

  **Acceptance evidence:** Import a Slack Pro workspace with two Slack Connect channels (`#acme-project` with 3 Acme people, `#vendor-print` with 1 vendor person whose email Slack does not supply) and one external DM with a third organization. The preview lists all three organizations, all five external people with their Slack user IDs and the missing vendor email, the external DM and each last-activity date. Choose invite for Acme and the vendor (entering the vendor's address, shown as owner-entered), and leave-in-Slack for the DM with the reason "client prefers Slack"; send at the cutover time with the Slack-side cutover message posted into both channels. An Acme person with no Nook installation confirms their mailbox and sees only `#acme-project`, with its imported history attributed to them through the Slack-user-ID mapping, the external badge on our side and our retention banner; they cannot browse channels, see our directory, or open `#vendor-print` or any DM. They post, and our members get the reply under ordinary NOOK-032 levels on phone and desktop. Removing them, or disconnecting Acme, revokes access on every client and API. The cutover report lists Acme 3 of 3 joined, the vendor 0 of 1 pending with one-action resend, and the DM as left in Slack with its reason, and the checklist closes only once every Slack Connect conversation has a disposition. A second run where a partner runs Nook invites it by installation address, and after both approve, the imported channel becomes a NOOK-031 shared channel with history intact. A message the Acme partner posts in Slack after the first import arrives with the NOOK-077 final pass, attributed through the same Slack-user-ID mapping.

- **NOOK-077 — Final catch-up import at cutover.** A staged Slack cutover loses nothing posted in Slack between the first import and the switch. **Delta passes:** after a first NOOK-041 import, the owner runs one or more delta passes, each drawing on any of a later official export ZIP (any export date range, overlapping the earlier one or not), the owner's approved source grant and each consenting member's NOOK-041 grant. For every imported conversation and every newly found one, a pass reads from that conversation's watermark (its latest imported Slack `ts`) less an overlap window (default 7 days, owner-adjustable up to the conversation's full history), and re-reads `conversations.replies` for every imported thread whose `latest_reply` is later than the watermark, so replies to old imported threads are never missed. Reads go through a Slack app that is Marketplace-approved or created in the buyer's own workspace from Nook's published app manifest as an internal app, so Slack's reduced `conversations.history`/`conversations.replies` limits for non-Marketplace distributed apps never throttle a pass; the preview estimates each pass's duration from the measured request rate before the owner schedules it. **What a pass adds:** new channels, DMs and group DMs (each through NOOK-041's audience rules and consent, and Slack Connect conversations through NOOK-076's dispositions), new root messages, new replies in already-imported threads, reactions, pins, file bytes under NOOK-041's file rules, and new Slack members, who join the cutover invitation preview for the owner's review. A member deactivated in Slack since the first import is flagged to the owner, and their pending invitation is withdrawn only by the owner's choice. **Deduplication:** every record is keyed by its Slack source identity (workspace, conversation and `ts`; file ID), so re-running a pass, or overlapping export and grant reads, adds nothing. **Changes in Slack:** an edit made in Slack to an already-imported message (a changed `edited.ts` or text) becomes a new imported revision with provenance (source, pass and Slack edit time), and the earlier imported revision stays in its history; reactions on imported messages follow Slack's latest re-read set. A message absent from a re-read that the source proves complete for that range (paged to its end without error) is tombstoned under NOOK-002 with provenance `deleted-in-slack`, removing its body from live revisions. A conversation or range that no source in the pass can re-read completely (no export coverage, no grant, a revoked grant or a Slack error) is reported `unverified` with its reason and the time it was last verified, and its imported messages are never tombstoned or changed on a guess. **Native activity is never overwritten:** Nook-native messages, replies, reactions (including those on imported messages, kept alongside Slack's), pins, saves, follows, read state, decisions and commitments are untouched by a pass; a decision or commitment citing an imported message that Slack later deleted keeps its own text and shows its source as deleted in Slack. Imported and native replies in one thread show in timestamp order, with native ones marked as posted in Nook. **Inert:** delta history creates no unread, Attention item, notification, automatic follow (NOOK-072), keyword hit, bot or workflow run, or accepted work, exactly like the first import; clients receive it through ordinary replay (NOOK-011) as history. NOOK-041's guarded batch undo applies per pass, removing only that pass's additions and revisions and never native activity. **Report and one-action switch:** every imported conversation shows "Slack history imported through <date and time, time zone>" (its latest completely verified read), and the cutover report states the workspace-wide figure with per-pass counts of added, revised, tombstoned, unverified and skipped records, each skip with its reason. The owner schedules the final pass for the cutover time, and that one scheduled action, surviving restart, runs the final pass and then sends the NOOK-041 invitations and the optional NOOK-076 Slack-side cutover message. If the final pass fails, the invitations wait and the owner sees why, then chooses to retry or to send anyway with the uncovered period stated in the report.

  **Acceptance evidence:** Import a Friday export of a Slack Pro test workspace, with member grants for its private channels and DMs. Over the weekend in Slack, post a new root message and a reply to an imported thread, edit one imported message, delete another, add a reaction, create a channel, start a new DM between two consenting members, add a member and create a Slack Connect channel with a partner. On Monday morning, before the delta pass, post native Nook replies in one imported thread. Run the final delta pass at the scheduled cutover time: each Slack change appears exactly once; the edit shows as a new imported revision with the earlier one in its history; the deleted message is tombstoned; the reaction, the new channel and the DM are present, and the DM is visible only to its two members; the new member appears in the invitation preview and is invited; the thread interleaves imported and native replies in time order with native ones marked; no one receives an alert, unread, Attention item or follow for imported items; and every native item is unchanged. The new Slack Connect channel appears in the NOOK-076 preview and keeps the checklist open until it has a disposition. Run the same pass again: it adds nothing. Revoke the grant covering one private channel and delete a message there in Slack: the next pass counts that channel as `unverified` with its reason and leaves the imported message intact. Force a Slack error during a scheduled final pass: the invitations wait and the owner sees the failure. The cutover report shows "imported through" the cutover time with per-pass counts, and undoing the last pass removes only its additions and revisions while the native replies remain.

- **NOOK-078 — Push from owned installations to the store apps.** Every customer installation, on its own Cloudflare account, pushes to the store-published iOS and Android apps without holding Apple's or Google's signing credentials. **Named push path:** the publisher (just-every) operates the Nook Push Gateway, which holds the store apps' APNs auth key (alert and PushKit VoIP topics) and FCM service account. Setup generates a per-installation Ed25519 key, registers its public half with the gateway against the installation's NOOK-074 descriptor (the gateway confirms that the installation's `/.well-known/nook-installation.json` names the same key), and signs every push request with it. The device token travels with each request, and the gateway keeps no device-token registry. Doctor and the admin health view show the registration state, the key fingerprint and the last gateway acknowledgement time. **What the gateway sees:** only the device token, installation ID, an opaque delivery ID, timing, push type, priority, TTL, collapse identifier and the encrypted packet's size. NOOK-062's per-device encryption, badge count included, is applied before a request leaves the installation, and the visible fallback text APNs and FCM need when a device cannot decrypt is the fixed `generic` wording, so no message text, sender or conversation name, file name or badge count reaches the gateway, APNs or FCM. The gateway logs no per-message record beyond per-installation rate counters and delivery-result counts kept at most 72 hours, then only daily aggregate counts per installation. **Disclosure (NOOK-050):** the gateway is on the published subprocessor list with its region (US, on the publisher's Cloudflare account, with transient request processing at Cloudflare's edge stated), its published availability objective (99.95% monthly, measured and published), its price (included at USD 0 for every installation, with no per-member or per-installation fee) and its terms: at least 12 months' public notice before any price change or discontinuation, with owner-credential mode available throughout. **Acknowledgements and failure handling:** the gateway returns APNs's and FCM's own result (accepted with the provider's ID, or the provider's error) to the installation. A stale token (APNs `Unregistered` or `BadDeviceToken`, FCM `UNREGISTERED`) retires that device under NOOK-021's repair rules. A gateway refusal, rate limit or outage is recorded as `provider-unconfirmed` with reason `gateway` in the member's device delivery view and the owner's delivery report (NOOK-048), never as a silent miss, and doctor and the admin health view (NOOK-049) show a named dependency outage. Queued pushes retry with the same delivery ID within each event's TTL, and the apps display each delivery ID at most once, so recovery creates no duplicates; an event whose TTL lapses (a call invite after 30 seconds) stays `provider-unconfirmed`. **Revocation:** the owner can revoke or rotate the installation's gateway registration from setup. The publisher may suspend a registration only for abuse under its published acceptable-use terms, with the reason shown in doctor. A revoked or suspended registration sends nothing and doctor says why. **Owner-credential mode:** an owner who wants no publisher in the push path builds the iOS and Android apps from the published build instructions under their own bundle ID and Firebase project, signs them with their own Apple Developer and Google Play accounts and distributes them (Apple Business Manager custom app, managed Google Play private app or MDM) with the installation address preset. Setup accepts their APNs `.p8` key (key ID, team ID and bundle ID) and FCM service-account JSON, stores them encrypted like other provider secrets and verifies each before saving: APNs must answer a probe with `BadDeviceToken` rather than `InvalidProviderToken`, and FCM must accept a `validate_only` send. The installation then sends to APNs and FCM directly, with the same encryption, acknowledgements and failure accounting. Desktop apps receive over their own authenticated connection to the installation and the web/PWA through NOOK-015's VAPID Web Push, so neither depends on the gateway.

  **Acceptance evidence:** Install Nook on a fresh Cloudflare account that is not the publisher's, run setup and see the gateway registration, key fingerprint and last acknowledgement in doctor. Sign the store-published iOS and Android apps in through NOOK-074 and run NOOK-021's 500-event delivery-bar journey from that installation: it meets the bar. Capture every request the gateway receives during the run: none contains message text, a sender or conversation name, a file name or a badge count. Block the gateway for 10 minutes during the run: the affected events are recorded as `provider-unconfirmed` with reason `gateway`; the owner report, the admin health view and doctor name the gateway outage; when the block lifts, delivery resumes with no event displayed twice; and a call invite whose 30-second TTL lapsed stays `provider-unconfirmed`. Uninstall the app on one phone and see its token retired. Revoke the installation's gateway registration: pushes stop and doctor says why; re-register and they resume. Repeat a reduced 50-event journey in owner-credential mode with apps self-built and signed by the buyer's own Apple and Google accounts: setup rejects a wrong `.p8` key and accepts the right one, and a capture shows no request reaching the gateway. The published subprocessor list names the gateway with its region, availability objective, price and terms.

## Push further: accountable agents in the conversation

- **NOOK-054 — Scoped company-context agents and MCP.** Install named agent participants with an accountable human owner, explicit channel membership, source grants and an allow-list of tools. Provide a spec-conformant MCP server: a pinned MCP protocol version, OAuth 2.1 authorization per member or agent, and versioned tools `list_conversations`, `search_messages`, `read_conversation`, `read_thread`, `post_message` and `propose_commitment`, so any standard MCP client (for example Claude, ChatGPT or Cursor, or Rocket.Chat or another chat product acting as an MCP client) connects without Nook-specific code under the caller's own permissions; Nook agent participants act as MCP clients only toward MCP servers an owner has approved; expose granted channels, document sources, delegated users and allowed writes before activation. Agent provisioning, OAuth/token rotation, run budgets, context retention, audit and pause/revoke are administrative controls. Retrieval intersects agent, requester, source and destination permission; private evidence cannot be laundered into a broader channel or another agent’s memory. External writes require an exact scoped grant and human approval according to policy; inbound content never changes those grants.

  **Acceptance evidence:** Provision a channel agent and read-only MCP client, connect a permission-scoped document and request a summary/post. Deny access to a second private channel, an unlisted tool and a malicious source instruction; revoke membership/grants mid-run and verify no cached context or further tool write escapes. Run the official MCP Inspector against Nook's MCP server and pass its tool-listing and tool-call checks; connect two independent standard MCP clients, one authorized as a member and one as an agent, and see each return only its own permitted messages; an agent's call to an MCP server the owner has not approved is refused.

- **NOOK-055 — Accountable commitment assistant.** An agent in its granted conversation reads agreed asks and authorized linked records, identifies a possible assignee, scope, deadline, dependencies and source evidence, then proposes a commitment for review. A casual mention never creates accepted responsibility; only the named assignee accepts the exact version. After acceptance, the agent tracks due time and dependency evidence, asks for missing information and proposes revisions/cancellation or a completion report. Only the assignee reports completion; provider issue closure is evidence, never impersonated human acceptance or verified delivery. Users can dismiss extraction, pause follow-up or revoke grants; revisions renew acceptance.

  **Acceptance evidence:** Discuss a vague request, a declined ask and an agreed ask. The assistant proposes only a cited candidate; deny author/agent acceptance on behalf of the assignee. Accept, revise a due date and close linked work: require renewed agreement and assignee completion, then pause the agent and verify no further chase or external write.

- **NOOK-056 — Decisions that stay current.** An authorized agent links human decisions to original discussion and explicitly connected project/document evidence, monitors later changes for concrete contradiction or stale assumptions and brings a cited proposed resolution to the responsible author and affected authorized participants. Distinguish a possible contradiction from a proven policy change; show current versions, dates and missing sources. Only a human decision author revises the decision through version comparison. Narrow private evidence stays in its permitted conversation, with no revealing cross-channel alert. Dismiss, snooze, pause and grant revocation stop further monitoring as selected.

  **Acceptance evidence:** Record a decision, change its linked authorized project/document to contradict it and produce a source-cited proposal for the original author. Make one source private and one stale, dismiss a false positive and revise the decision as its author; deny automatic/foreign revision and any wider audience leak.

- **NOOK-057 — Calm follow-through across linked systems.** For accepted commitments only, an agent reconciles permitted GitHub/Jira/Asana/Vector issue/PR state, HappyToHelp/Zendesk support events, Salesforce context and Uptime/Lamp deployment/alert evidence against the promised outcome. Batch routine updates, flag a concrete blocked dependency and bring the owner a cited next-step proposal. Never equate issue closure, support status or incident recovery with fulfilled human agreement. Recipient quiet time, urgency preferences, escalation recipients, budgets and pause apply; each external write needs an exact scoped reviewed grant. Stale or unavailable sources are shown as unknown and never repaired with invented success.

  **Acceptance evidence:** Accept a commitment linked to a PR and support case, deliver duplicate and out-of-order source events, then close/reopen one dependency and revoke another connector. Receive one calm evidence-linked update, an accurate blocked/unknown status and a proposed resolution; deny unauthorized writes and preserve assignee-only completion.

- **NOOK-058 — Stale handoffs and proposed resolution.** Within authorized conversations and accepted dependencies, detect an unacknowledged handoff, missing acceptance, overdue response or conflicting responsible party using explicit due times and the latest permitted evidence. Bring the right people a concise proposed owner/date/next action with citations and uncertainty, ask the proposed assignee to accept and let participants resolve, dismiss or snooze. Escalation rules have an accountable human owner, named recipients, quiet policy and stop condition; the agent cannot reassign, widen an audience or create an accepted promise itself.

  **Acceptance evidence:** Leave a handoff unaccepted, let an accepted dependency become overdue, then provide late evidence and an alternate suggested assignee. The agent separates those states, proposes a cited resolution to the permitted people, stops after resolution/pause and requires the new assignee’s own acceptance.

- **NOOK-059 — Visible and controllable agent execution.** Every scheduled or interactive agent obligation exposes its human owner, purpose, current source grants, run/version, due time, budget, approval, planned actions and stop condition. Runs survive restart with bounded reconciliation and record output citations, tool attempts, provider-confirmed/failed/uncertain effects and user decisions. Pause/revoke prevents new calls and writes, cancels outstanding approval and names any in-flight effect that cannot be recalled. A failed model/provider/tool surfaces a failed or incomplete run with a human resolution path; no blind retries of an uncertain write, hidden success fallback, self-granted tool or unaudited autonomous completion.

  **Acceptance evidence:** Start a scheduled handoff run, inspect and decline a proposed external write, interrupt the model/provider after a step, restart and revoke the grant. The owner sees a durable failed/uncertain state and exact remaining action; no duplicate write, resumed revoked access or unapproved commitment appears.

## Honest boundaries and external custody

- Nook delivers messaging, integrated calls, clients, apps, shared channels, administration and agent follow-through itself. LiveKit Cloud/Twilio may supply media transport/TURN; Apple (APNs, PushKit), Google (FCM) and browser vendors supply push transport, and the publisher-operated Nook Push Gateway (NOOK-078) holds the store apps' push credentials and relays sealed packets for owned installations, or the owner's own credentials and self-built apps take it out of the path; Zoom/Meet/Teams supply linked meetings. Nook owns their admission, context, failure handling and client journey. It does not operate a PSTN carrier, emergency/911 service or public webinar business; buyers obtain those services through their licensed calling/meeting provider. Huddles and native calls remain required.
- Retention, legal hold, controlled compliance export and DLP are product obligations. Legal advice, formal certification and a regulated health-data service require the organization’s counsel, qualified archive/eDiscovery partner, suitable deployment and provider agreements; a US HIPAA/BAA or certification claim needs its own evidence. Purview/export partners complement Nook’s controls and do not replace them. A hold is an explicit preservation exception to erasure, not a silently completed deletion.
- Nook owns conversation context and accepted commitments, while provider/sibling systems remain authoritative for documents, tickets, projects, CRM, calendars and incident state. Google Drive, Microsoft 365, Notion, GitHub, Jira, Asana, Salesforce, Zendesk and the linked siblings retain their source permissions and provider terms. Integration disconnection cannot delete a copy or undo a write already accepted by another service; show the remaining custody and reconciliation duty.
- Export authority is bounded by the source export and the organization’s approved audience. No adapter invents missing history or converts unknown memberships into public access. All six named incumbent/up-and-comer migrations remain obligations even where provider access, export rights or schema work is difficult.
- Offline cache and external archives require explicit owner/device custody. An offline device cannot receive a revocation before reconnect; downloaded exports, already sent notifications, screenshots and provider backups cannot be retroactively recalled. Current live erasure and protected preserved copies have distinct disclosed duties.
- Malware scanning (NOOK-061) reports signature/heuristic results: a clean label means no known threat was detected, never a guarantee. Platform limits, provider outages, US hosting/subprocessor choices and actual USD usage are disclosed. No guaranteed human response, unlimited scale, WCAG certification, end-to-end encryption or regulatory approval is implied by a local test or marketing phrase. Native apps and reliable push, media, source integrations and admin controls stay in scope until proved.

## Status

Judged from the code and tests at commit `310ecfea1604135bdcbbabafcc167057ec6b873b` (19 October 2026). NOOK-071, NOOK-052, NOOK-072, NOOK-047 and NOOK-046 web evidence updated on 19 October 2026 from the journeys cited in their rows. The 0.4.0 release gate (19 October 2026, commit `63ceb678`) changed no status; it narrowed only the NOOK-008 and NOOK-014 gaps to what it proved.

Current code/test coverage is recorded per clause below and mirrored in ACCEPTANCE.md. Evidence identifies the implemented subset; a partial row’s Gap records the broader acceptance still required, including where a shipped subset is covered by another clause. These statuses do not reduce the requirements above.

| Clauses | Done | Partial | Missing | Total |
| --- | ---: | ---: | ---: | ---: |
| Published | 15 | 2 | 0 | 17 |
| Target and push further | 9 | 20 | 34 | 63 |
| All | 24 | 22 | 34 | 80 |

| Clause | Title | Kind | Status | Evidence | Gap |
| --- | --- | --- | --- | --- | --- |
| `NOOK-001` | Current identity and conversation membership (C1) | published | done | `src/platform/request.server.ts`; `src/platform/storage/context.ts`; `tests/operator-security.mjs`; `tests/storage-guards.mjs` |  |
| `NOOK-002` | Atomic messages, retries and replay (C2) | published | done | `src/platform/storage/messages.ts`; `src/platform/storage/commands.ts`; `tests/storage-flows.mjs` |  |
| `NOOK-003` | Fixed DMs and channel membership (C3) | published | done | `src/platform/storage/commands.ts`; `src/team/channels.server.ts`; `tests/team-runtime.mjs`; `tests/storage-flows.mjs`; `tests/browser.mjs` |  |
| `NOOK-004` | Decisions and commitments (C4) | published | done | `src/platform/storage/outcomes.ts`; `src/platform/storage/outcome-history.ts`; `tests/storage-flows.mjs`; `tests/browser.mjs` |  |
| `NOOK-005` | Read cursors and attention (C5) | published | done | `src/platform/storage/read.ts`; `src/chat/useReadVisibility.ts`; `tests/storage-flows.mjs`; `tests/browser.mjs` |  |
| `NOOK-006` | Offline restore (C6) | published | done | `src/platform/storage/portability.mjs`; `scripts/local/transfer.mjs`; `scripts/storage/recover-identities.mjs`; `tests/recovery.test.mjs`; `tests/pilot-journey.mjs` |  |
| `NOOK-007` | Safe rendering and local binding (C7) | published | done | `src/platform/operator-auth-runtime.mjs`; `src/platform/request.server.ts`; `src/chat/MessageRow.tsx`; `tests/team-security.test.mjs`; `tests/operator-security.mjs` |  |
| `NOOK-008` | Evidence limits (C8) | published | partial | `ACCEPTANCE.md`; `tests/push-journey.mjs`; `tests/pilot-journey.mjs` | The 0.4.0 release gate reran the local core journeys at 1440 and 390 and recorded one hosted run on the RunEditRun Cloudflare account: real sign-in, invitation and mention mail, an update from a populated 0.3.0 installation applying every later migration, and hosted export, removal, redeploy and local restore. Hosted multi-device, real-device push, capacity and full-category acceptance remain unproven. |
| `NOOK-009` | Backup and export (C9) | published | done | `src/platform/portability.server.ts`; `src/platform/storage/portability.mjs`; `tests/exit-export.test.mjs`; `tests/team-portability.test.mjs` |  |
| `NOOK-010` | Commitment revision and cancellation (C10) | published | done | `src/platform/storage/outcomes.ts`; `src/platform/storage/outcome-history.ts`; `tests/storage-flows.mjs`; `tests/erasure.test.mjs` |  |
| `NOOK-011` | Cached retries, synchronization and complete history (C11) | published | done | `src/platform/storage/history.ts`; `src/platform/storage/commands.ts`; `src/platform/storage/live.ts`; `tests/storage-flows.mjs`; `tests/browser.mjs` |  |
| `NOOK-012` | Private attachments (C12) | published | done | `src/platform/storage/files.ts`; `src/team/preview.server.ts`; `src/team/ImagePreview.tsx`; `tests/team-image.test.mjs`; `tests/storage-flows.mjs`; `tests/team-browser.mjs` |  |
| `NOOK-013` | Calm attention (C13) | published | done | `src/family/team-chat/attention.ts`; `src/platform/storage/commands.ts`; `tests/attention.mjs`; `tests/storage-flows.mjs`; `tests/browser.mjs` |  |
| `NOOK-014` | Email notifications (C14) | published | done | `src/notify/run.ts`; `src/notify/queue.ts`; `src/notify/compose.ts`; `tests/notify-journey.mjs`; `tests/notify.test.mjs` | Done for the specified behavior. In the 0.4.0 release gate one hosted mention email reached a real inbox through the owner deployment’s SendGrid sender, naming sender and channel without message text; that single receipt establishes no general deliverability or inbox-receipt guarantee. |
| `DOCS-001` | Public documentation from Markdown | published | done | `src/docs/build/site.ts`; `src/docs/build/render.ts`; `docs/site.json`; `tests/docs-site.test.mjs` |  |
| `ERASURE-001` | Member, message and imported-author erasure | published | done | `src/team/erasure/service.ts`; `src/team/erasure/graph.ts`; `src/team/erasure/auth-identity.ts`; `src/team/erasure/identity.ts`; `tests/erasure.test.mjs`; `tests/erasure-browser.mjs` | Native/imported cascade, verified blob absence, credential/grant/provider-session removal, independently anonymized auth audit and journal refusal are covered. Logical erasure retains the clause’s stated external-copy and physical-storage limits. |
| `NOOK-015` | Web Push (C15) | published | partial | `src/push/run.ts`; `src/push/subscription.ts`; `src/push/crypto.ts`; `tests/push/journey.mjs`; `tests/push/packet.mjs`; `tests/push-service-worker.test.mjs` | Bounded local Web Push and PWA ship, and enabling push turns on mentions (`src/push/client.ts`); keyword hits and call invites, NOOK-073 cross-device routing in place of the all-device visible-page hold, and browser-vendor/OS receipt and real-device installation evidence are missing. Native transport belongs to NOOK-021 and its push path to NOOK-078. |
| `NOOK-016` | Everyday conversations | target | done | `src/chat/NewMessage.tsx`; `src/team/channel-lifecycle.server.ts`; `src/team/ChannelSettings.tsx`; `src/team/channels.server.ts`; `src/team/ChannelBrowser.tsx`; `src/team/default-channels.server.ts`; `tests/browser.mjs`; `tests/group-dm.mjs`; `tests/channel-lifecycle.mjs` | App-created fixed group DMs, roots/replies/reactions, context links, own edit/delete, scoped discovery/join/leave, private transfer/readmission and mandatory defaults are covered at 1440/390. Archived public channels remain discoverable as read-only history after their last member leaves; a current owner/admin joins before restoring. |
| `NOOK-017` | Composition and personal workspace | target | partial | `src/chat/Composer.tsx`; `src/chat/composition/useCompositionDelivery.ts`; `src/commands/useCommands.tsx`; `src/profiles/server.ts`; `src/personal/workspace.server.ts`; `src/personal/typing.server.ts`; `tests/composition-browser.mjs`; `tests/profiles.mjs`; `tests/personal.mjs`; `tests/personal-browser.mjs`; `tests/team-portability.test.mjs`; `tests/storage-flows.mjs` | Web Markdown source with accessible rich preview, typed-format conversion/undo, person/channel mentions, emoji, text export, exact-byte previewable snippets and resumable split sends ship alongside durable Drafts & sent. Built-in slash commands use current-authority APIs and private confirmations. Profiles, chosen time zones, expiring statuses, optional typing/presence, field privacy, a fully paged 5,001-person directory, private sidebar sections, explicit mark unread and installation links ship. Browser journeys cover 1440/390 and lost-send acknowledgement across reload; archive/erasure retain their guarantees. The conversation-list cap no longer limits draft enumeration. Remaining: Native-client draft/status/workspace-switch journeys, calling/GIF service effects for `/huddle` and `/gif`, app-command ordering/name reservation and Slack-compatible length errors remain under NOOK-021/022/025/063/070. Installation switching is by separate-origin links and independent sign-in; no native workspace picker is claimed. |
| `NOOK-018` | Invitations, roles, guests and offboarding | target | partial | `src/team/invitations.server.ts`; `src/team/access.ts`; `src/platform/operator-auth-runtime.mjs`; `tests/team-runtime.mjs`; `tests/operator-security.mjs` | One-address confirmed email invitations, member/guest grants, deactivation and explicit regrant ship; bulk/CSV invitations, invitation links, approved-domain join and invitation requests are missing. |
| `NOOK-019` | Search conversations and files | target | done | `src/search/server.ts`; `src/search/parse.ts`; `src/search/terms.ts`; `src/search/files.ts`; `src/chat/SearchView.tsx`; `src/files/server.ts`; `src/files/FilesView.tsx`; `tests/search.mjs`; `tests/search-browser.mjs`; `tests/files.mjs`; `tests/files-browser.mjs`; `tests/saved-search.mjs`; `tests/slack-import/journey.mjs` | Unicode terms of every length, phrases, token prefixes, substrings, exclusions, typed modifier chips, time-zone dates, conversation-type filters and live native/imported file-name/text search ship. Unsupported/unindexed file contents are named; PDF extraction is bounded to 100 pages and 1 MiB of text. An authenticated 80,000-message corpus pages completely; 1440/390 journeys cover result context, errors and revoked access. Native clients and wider file admission remain NOOK-021/061. |
| `NOOK-020` | Saved items and channel pins | target | done | `src/team/saved.server.ts`; `src/team/saved-policy.ts`; `src/team/saved-reminders.server.ts`; `src/chat/SavedView.tsx`; `src/chat/SavedReminder.tsx`; `src/chat/ReminderCards.tsx`; `src/chat/PinsView.tsx`; `tests/saved-search.mjs`; `tests/saved-reminders.mjs`; `tests/saved-reminders-browser.mjs`; `tests/team-portability.test.mjs` | Private ordered message/file/link saves, shared pins with actor/time, due reminders with edit/cancel/snooze/dismiss, restart and second-session recovery, quiet holds, current-ACL firing and erased-source tombstones are covered on the web at 1440/390. Reminders create no unread, external notification or accepted work. Native clients remain NOOK-021’s separate missing implementation. |
| `NOOK-021` | Native clients and dependable device catch-up | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-022` | In-context voice, video and screen sharing | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-023` | Zoom, Google Meet and Teams meetings | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-024` | Asynchronous clips, transcripts and call artifacts | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-025` | App and bot platform | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-026` | Workflow builder and durable scheduled work | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-027` | Engineering and project integrations | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-028` | Support, sales and operational signals | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-029` | Connected documents and knowledge | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-030` | Calendars, availability and meeting reminders | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-031` | Shared channels between organizations | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-032` | Recurring quiet schedules and notification policy | target | partial | `src/team/conversation-notify.server.ts`; `src/chat/ConversationNotifications.tsx`; `src/platform/storage/messages.ts`; `src/push/policy.ts`; `src/notify/run.ts`; `tests/threads.mjs`; `tests/push/routing.mjs`; `tests/notify-journey.mjs` | Web conversation levels (all new messages, mentions, nothing), all-thread replies, mute exclusivity, exception list/reset, current-ACL routing, delivery-time cancellation and durable archive/erasure integration ship. Nothing silences pending/new email and push while explicit mentions/urgent requests remain in Attention. Web Push phone/tablet level and thread overrides use explicit per-device classification under NOOK-072. Recurring/time-zone schedules, keywords, native classification and active-elsewhere routing, channel-broadcast replies, org/new-member push defaults and email onboarding remain missing. |
| `NOOK-033` | Personal and channel reminders | target | done | `src/reminders/service.ts`; `src/reminders/runner.ts`; `src/reminders/parse.mjs`; `src/reminders/ReminderEditor.tsx`; `src/reminders/RemindersView.tsx`; `tests/reminders.mjs`; `tests/reminders-browser.mjs`; `tests/exit-export.test.mjs` | Personal plain-text/message/file and channel reminders, previewed natural-language times, recurrence, current-audience alarm delivery, edit/snooze/complete/cancel, restart, quiet holds, visible failure and paused restore ship on the web at 1440/390. Native clients remain NOOK-021’s separate missing implementation. |
| `NOOK-034` | Scheduled and recurring messages | target | done | `src/scheduled/service.ts`; `src/scheduled/runner.ts`; `src/scheduled/files.ts`; `src/scheduled/time.mjs`; `src/scheduled/ScheduleEditor.tsx`; `src/scheduled/ScheduledView.tsx`; `tests/scheduled.mjs`; `tests/scheduled-browser.mjs`; `tests/erasure.test.mjs`; `tests/exit-export.test.mjs` |  |
| `NOOK-035` | Channel recaps and catch-up digests | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-036` | AI search with cited answers | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-037` | SSO, SAML and lifecycle provisioning | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-038` | Retention, legal hold and preservation | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-039` | Compliance search and controlled exports | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-040` | Information protection and data handling | target | partial | `src/platform/storage/context.ts`; `src/platform/storage/files.ts`; `src/push/crypto.ts`; `tests/team-security.test.mjs` | Current-membership private-message/file access and encrypted push exist; labels, DLP, organization AI policy and cross-tool enforcement are missing. |
| `NOOK-041` | Slack migration and cutover | target | partial | `src/imports/archive-plan.mjs`; `src/imports/history.ts`; `src/imports/ImportWizard.tsx`; `src/imports/slack.mjs`; `tests/slack-import/journey.mjs`; `tests/pilot-journey.mjs` | Reviewed resumable public-channel JSON ZIP history, file choices, threads, bot provenance and guarded undo ship on synthetic fixtures; private/DM/shared import (including the consented member-token path for Free/Pro exports), custom emoji and user-group import, the integration inventory with dispositions, Slack-link resolution in imported history, personal reminder and starred-item import, full metadata parity and real-source cutover remain missing. |
| `NOOK-042` | Microsoft Teams migration | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-043` | Google Chat migration | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-044` | Campfire ONCE migration | target | done | `src/imports/campfire.mjs`; `src/imports/campfire-records.mjs`; `src/imports/campfire-capture.mjs`; `src/imports/campfire-effects.mjs`; `src/imports/campfire-reviewed.mjs`; `src/imports/service.ts`; `src/imports/CampfireWizard.tsx`; `docs/campfire-import.md`; `tests/campfire-import/fixtures/README.md`; `tests/campfire-import/journey.mjs`; `tests/campfire-import/browser.mjs` | None. Version-declared consistent official Campfire Express format-1 SQLite/upload backups are reviewed through the shared import pipeline with exact public/private/DM kinds, mapped participant sets and fixed DM rosters, source authors and saved times/rich text, provenance and original file SHA-256. The genuine upstream HTTP-created fixture reconciles five rooms, four identities, ten messages and three files; unsupported/incomplete or ambiguous backups are refused before publication. Explicit injective identity association creates no account, credential or source-role grant and adds no importer to a DM. Durable apply/lost-ack resume avoids duplicates; clean guarded undo removes only owned unchanged effects, and native activity blocks undo without losing later messages. Both-width UI journeys include the guide, review, completion and populated imported history. The supported source version has no native reply relationship; none is invented. Rails backup layouts and other schemas are explicitly unsupported; declared size/record bounds refuse rather than truncate. |
| `NOOK-045` | Zulip migration and topic continuity | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-046` | Mattermost migration | target | done | `src/imports/mattermost.mjs`; `src/imports/mattermost-records.mjs`; `src/imports/mattermost-capture.mjs`; `src/imports/mattermost-effects.mjs`; `src/imports/mattermost-reviewed.mjs`; `src/imports/MattermostWizard.tsx`; `docs/mattermost-import.md`; `tests/mattermost-import/fixtures/README.md`; `tests/mattermost-import/journey.mjs`; `tests/mattermost-import/browser.mjs` | None. Supported Mattermost Server 12 bulk format-1 ZIPs preserve teams, public/private channels, fixed DM/group rosters, original authors and millisecond times, replies, supplied reactions, checksum-verified attachments/custom emoji, private saves and shared channel pins. Every source role is explicitly retained as history only; identity mappings create no account, credential or custodian grant. The genuine official-image deployment fixture reconciles 73 source records, nine conversations, four people/two built-in bots, 33 messages, three files and one custom emoji. Upstream omits direct/group root reactions: the API readback retains its four-reaction expectation, while the ZIP supplies three; preview explicitly names the unknown source omission, and supplied direct reactions are independently exercised. Seven clause-tagged emitted Worker/D1 checks cover refusal of incomplete/unsupported/ambiguous inputs, exact audiences and hashes, inert history, an actual interrupted multi-batch checkpoint/restart with deduplication, guarded undo preserving native activity, clean reversal, and browser use at 1440/390. Original source credentials never become Nook authority; private saved identities are excluded from ordinary provenance/readable exports. Source bounds and unknown source facts are documented rather than silently truncated or invented. |
| `NOOK-047` | Readable portability and controlled exit | target | done | `src/exit/authority.ts`; `src/exit/store.ts`; `src/exit/pages.ts`; `src/exit/files.ts`; `src/exit/catalog.ts`; `src/exit/format.ts`; `src/exit/ReadableExport.tsx`; `src/exit/OwnerExportPolicy.tsx`; `migrations/0062_workspace_readable_export.sql`; `docs/readable-export.md`; `tests/readable-export.test.mjs`; `tests/readable-export-archive.test.mjs`; `tests/readable-export-browser.mjs` | None. Versioned JSON/JSONL and self-contained offline HTML cover member conversations and owner-authorized whole-workspace exit, complete paged native/imported history, membership records, versions/tombstones, reactions, decisions, acceptance history, source mappings and checksum-verified file bytes. Policy changes atomically announce their version to every active member and audit owner/time/version/recipient count; each workspace export states that version. Workspace authority is default-off, explicitly enabled by a current owner, version-fenced on every report/page/file and reset on operational restore; ordinary admins remain refused another person’s DM. Cancel/reload/resume, exact counts, offline reading and both-width journeys name NOOK-047. Non-secret workspace policies are included; unpublished sources, credentials, held copies, unsupported external artifacts, unrecorded pre-adoption history and not-yet-shipped workflow/app/agent records are explicitly named omissions. No copied bodies or file bytes persist in jobs; browser assembly needs memory for the selected data. |
| `NOOK-048` | Team administration and reporting | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-049` | Reliability, performance and operating recovery | target | partial | `src/platform/storage/live.ts`; `src/platform/storage/messages.ts`; `scripts/storage/measure.mjs`; `tests/storage-flows.mjs`; `tests/recovery.test.mjs` | Normalized D1/R2, Durable Object replay and local restart/restore checks exist; hosted service levels, media/platform failure recovery, directory paging past 1,000 and the stated capacity objectives remain unproven. |
| `NOOK-050` | Owned installation and Base replacements | target | partial | `src/platform/operator-auth-runtime.mjs`; `src/platform/owner-claim.server.ts`; `src/platform/portability.server.ts`; `scripts/installer/local.mjs`; `tests/installer-workflow.mjs`; `tests/owner-deploy.test.mjs`; `tests/part6.mjs` | Base-backed local lifecycle, owner claim, portability and fake-account deployment checks exist; live hosted/provider/native operating proof, data-location choice, full cost reporting, the published subprocessor list naming the NOOK-078 gateway and the published 50/300-member reference cost with its gateway and owner-credential lines are missing. |
| `NOOK-051` | Auditable authority and actions | target | partial | `src/team/erasure/audit.ts`; `src/base/modules/operator-auth/accounts/admin.mjs`; `tests/erasure.test.mjs`; `tests/operator-auth.test.mjs` | Auth and anonymized erasure audit foundations exist; unified administrator log, hold/export/app/agent coverage and filters are missing. |
| `NOOK-052` | Accessible, responsive daily use | target | partial | `src/chat/QuickNavigation.tsx`; `src/chat/QuickSwitcher.tsx`; `src/chat/quick-threads.ts`; `src/chat/quick-people.ts`; `src/chat/useLastMessageEdit.ts`; `src/platform/storage/latest-edit.ts`; `src/chat/accessibility.css`; `tests/keyboard.mjs`; `tests/keyboard-gaps.mjs`; `tests/quick-switcher.mjs`; `tests/accessibility.mjs`; `src/reminders/RemindersView.tsx`; `tests/reminders-browser.mjs` | Web shortcuts, All unreads/mark-all undo, authoritative last-own-message editing in root/thread/inline composers, visible equivalents and IME/draft/upload/send guards ship. The quick switcher pages complete current-ACL conversation, people, public-channel and followed-thread doors, includes account-isolated ordinary thread visits after unfollow/reload and discards unavailable source labels. Focus, help, fuzzy matching, recency/unread ranking and 1440/390 journeys are covered. Reminder actions retain their write fence until the server’s current card version is loaded, so rapid snooze/cancel stays ordered. Remaining: native shortcuts/text scaling, manual VoiceOver/NVDA and full WCAG 2.2 AA evidence, plus accessibility acceptance for call/agent/native journeys as their underlying clauses ship. |
| `NOOK-053` | Conversation notes and lightweight lists | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-060` | Organization policy and channel lifecycle | target | partial | `src/team/channel-lifecycle.server.ts`; `src/team/default-channels.server.ts`; `src/team/DefaultChannels.tsx`; `src/team/ChannelSettings.tsx`; `tests/channel-lifecycle.mjs`; `tests/team-portability.test.mjs` | Versioned management transfer, name/topic, archive/restore, write fences and action history ship. Owners/admins configure public defaults with full-history disclosure and audit; only owners change mandatory requirements. Designated defaults cannot be archived until removed. Admission, archive recovery and erased audit attribution preserve audience. Delegated/custom roles, groups, description/privacy conversion, guest expiry, invitation/domain and creation/edit/delete/broadcast policy, custom emoji, group defaults and org notification defaults remain missing. |
| `NOOK-061` | Any office file, large uploads, previews and malware scanning | target | partial | `src/platform/storage/files.ts`; `src/team/preview.server.ts`; `tests/team-image.test.mjs`; `tests/storage-flows.mjs` | 25 MiB private R2 uploads, signature checks and PNG/JPEG preview exist; any-type 1 GiB resumable uploads, office/HEIC/GIF/video previews, malware scanning and admin file-type policy are missing. |
| `NOOK-062` | Notification previews and actionable replies | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-063` | Slack-compatible webhooks and Web API | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-064` | Zapier, Make and n8n connectors | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-065` | Channel-wide mentions: @channel, @here and @everyone | target | partial | `src/broadcast/audience.ts`; `src/broadcast/settings.ts`; `src/broadcast/presence.ts`; `src/platform/storage/message-statements.ts`; `src/broadcast/ConfirmBroadcast.tsx`; `tests/broadcast.mjs`; `tests/broadcast-browser.mjs`; `tests/push/broadcast.mjs`; `tests/notify/broadcast.mjs` | Web/API @channel, @here and @everyone ship with atomic saved audiences, confirmation, current-session presence/away, mandatory default-channel designation, owner/channel policy, named people, ignore/mute/quiet rules, recipient lists, email/encrypted Web Push choices and scheduled/reminder firing fences. User groups, app/webhook/workflow grants, native clients and real provider/device receipt remain unimplemented or unproven. |
| `NOOK-066` | Channel posting permissions and announcement channels | target | partial | `src/team/posting.server.ts`; `src/team/posting-policy.ts`; `src/team/PostingSettings.tsx`; `src/platform/storage/messages.ts`; `tests/posting.mjs`; `tests/posting-browser.mjs`; `tests/team-portability.test.mjs` | Web/API announcement permissions ship for everyone, owners/admins, channel manager or named members, with independent replies, atomic send/edit fences, labelled admin override, channel audit, archive validation and erasure of named grants. User groups, broadcast policy and app/webhook/workflow/scheduler/native write-door acceptance remain missing with their underlying features. |
| `NOOK-067` | Forwarding messages and email into conversations | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-068` | Safe link previews and app unfurls | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-069` | Two-factor authentication without SSO | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-070` | Polls, GIFs, thread broadcast, bookmarks and translation | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-071` | Threads view and group-DM conversion | target | done | `src/chat/ThreadsView.tsx`; `src/platform/storage/threads.ts`; `src/chat/AllUnreads.tsx`; `src/platform/storage/unreads.ts`; `src/team/group-conversion.server.ts`; `tests/threads.mjs`; `tests/unreads.mjs`; `tests/group-conversion.mjs`; `tests/unreads-browser.mjs` | Web Threads, paged All unreads, isolated/cross-session reads and ten-second undo ship at 1440/390. Conversion moves stable native history once, keeps the fixed source read-only, retains the complete original roster and audit, copies only current authorized access and makes the converter manager. Offboarded creators stay denied after reactivation until explicit channel regrant; archive validation and restart preserve this boundary. Conversation enumeration no longer has a 1,000-row cap. Native clients remain NOOK-021. |
| `NOOK-072` | Automatic thread following and channel-wide thread replies | target | partial | `src/platform/storage/threads.ts`; `src/platform/storage/thread-badge.ts`; `src/team/conversation-notify.server.ts`; `src/push/devices.ts`; `src/push/queue.ts`; `src/push/snapshot.ts`; `public/sw.js`; `tests/threads.mjs`; `tests/thread-delivery.mjs`; `tests/push-service-worker.test.mjs`; `tests/push/archive.mjs` | Web automatic follows include the committed broadcast audience; sticky unfollow, native continuation of imported roots, virtual all-thread views, phone/tablet overrides with explicit device classification, delivery-time cancellation and mentions-only unread/Web Push badges ship. First device registration enables supported mention/DM/thread defaults once and preserves deliberate opt-outs. Encrypted notifications offer authenticated recipient-checked unfollow for one thread, or Threads selection for a mixed burst. 1440/390 settings, restart and current-ACL paths are covered. Remaining: shared channels (NOOK-031), native clients/device receipt (NOOK-021/078), active-elsewhere routing (NOOK-073), recurring quiet schedules (NOOK-032) and preview policy (NOOK-062) with their underlying clauses. Import remains inert. |
| `NOOK-073` | Active-elsewhere device routing | target | partial | `src/push/policy.ts`; `src/push/devices.ts`; `tests/push/presence.mjs` | A fresh, session-bound visible-page heartbeat holds all of the member's Web Push and an alarm releases it at 45-second expiry; input- and lock-based desktop activity, routing by device class, release delays, "always send to mobile", call/urgent bypass, native heartbeats and `held-active-elsewhere` records are missing. |
| `NOOK-074` | Native sign-in and finding the installation | target | partial | `src/base/modules/operator-auth/identity/email-link/confirmation.mjs`; `src/team/invitations.server.ts`; `src/base/modules/operator-auth/identity/oidc/presets.mjs`; `tests/operator-auth.test.mjs`; `tests/operator-security.mjs` | Browser magic-link sign-in and invitations ship, with confirmation GET/HEAD never consuming a link and native POST consuming once; the Base OIDC module copy has Google and Microsoft presets but Nook does not wire it. Installation descriptors, open-in-app links and QR codes, managed deployment, email codes, universal and app links, QR sign-in from desktop, personal providers and system-browser native SSO are missing. |
| `NOOK-075` | External participants without a Nook installation | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-076` | Slack Connect cutover | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-077` | Final catch-up import at cutover | target | partial | `src/imports/ids.mjs`; `src/imports/service.ts`; `tests/slack-import/journey.mjs` | Imported records get deterministic IDs from their Slack source identity, an interrupted import resumes, a second apply of the same upload is refused and guarded undo preserves native activity; delta passes from a later export or grants, Slack-side edits as revisions, deletion tombstones with `unverified` accounting, imported/native thread interleaving, the "imported through" report and the scheduled one-action switch are missing. |
| `NOOK-078` | Push from owned installations to the store apps | target | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-054` | Scoped company-context agents and MCP | push-further | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-055` | Accountable commitment assistant | push-further | partial | `src/platform/storage/outcomes.ts`; `src/family/team-chat/attention.ts`; `tests/storage-flows.mjs`; `tests/browser.mjs` | Human proposals, exact assignee acceptance, revision and completion reports ship; agent extraction, evidence tracking and controls are missing. |
| `NOOK-056` | Decisions that stay current | push-further | partial | `src/platform/storage/outcomes.ts`; `src/platform/storage/outcome-history.ts`; `tests/storage-flows.mjs` | Human versioned decisions and source links ship; contradiction monitoring and proposed agent resolution are missing. |
| `NOOK-057` | Calm follow-through across linked systems | push-further | partial | `src/notify/run.ts`; `src/family/team-chat/attention.ts`; `src/platform/storage/outcomes.ts`; `tests/notify-journey.mjs`; `tests/attention.mjs` | Human obligations and calm routing ship; connected-system reconciliation, batched agent updates and external-action grants are missing. |
| `NOOK-058` | Stale handoffs and proposed resolution | push-further | missing |  | No implementing source/test; acceptance journey above is pending. |
| `NOOK-059` | Visible and controllable agent execution | push-further | missing |  | No implementing source/test; acceptance journey above is pending. |
