← all docs · north-os-netdocs-filing-spec7 sept. 2026

NetDocuments filing back + folder watch — design spec

Status: planned Author: Claude (Fable, then Opus after the Fable credit stop), for Christophe Date: 2026-09-07 (R2 the same day, after two adversarial design reviews) Repo: north-os Related: 2026-08-17-netdocuments-mcp-federation-design (§3.3 consent, §6 persistence, §7 inability to write, §9 scope), 2026-08-27-netdocs-firmwide-connection-design (§2.1 access_scope, §2.2 exclusivity ruling and grandfathered rows, §2.4 reconnect and ownership transfer), 2026-06-10-document-editing-design (in-tool confirmation, resume, personal deliver route, agent rows never promoted), docs/reviews/2026-09-07-partner-netdocs-gating-triage (lane L6), PR #216 (REST connector; its client package and its §9 ledger are the live-verified REST facts), docs/connections/netdocuments-api-access-request.md

Revision R2, 2026-09-07 — after the second dual review (48 findings, both readers NOT CONVERGED)

R1 fixed the first round's write-safety gaps. R2 fixes what the second round found underneath them: the parts where the design named a mechanism that does not exist in the shape the design assumed. The deltas, so a third reader checks these rather than re-deriving them:

Revision R1, 2026-09-07 — after the first dual review (49 findings, both readers NOT CONVERGED)

Kept for the record; every R1 delta stands unless R2 changes it above. Claim-before-upload instead of a unique index after it; confirm_preapproved never honoured; a strict review binding instead of a toolCallId match; thread-reachability source authorization; bytes on the wire so no R2 client enters the credential boundary; owner-only v1; the MCP write loop gated by one family registry; snapshot diff instead of a watermark (ordering ignored live, cursor unproven); owner-isolated watch tables; a poller sub-capacity against the real limiter; newsletter-style delivery claims; a watch lifecycle backend; the worker custody surface stated; phase 0 gated on the firm's answer; §8 and the ledger extended.

0. Why

The partner's session on 2026-09-06 ran into three things North cannot do with NetDocuments: browse, file a document back, and notice when something lands in a matter folder. Browse and ids are lane L5. This spec covers the other two, which the triage report left as "design spec only, consent decision with the firm before any code":

  1. Filing back. One write tool that uploads a document North holds into a NetDocuments folder the lawyer chose beforehand, behind a structural confirmation, fully audited.
  2. Folder watch. One polled watch per flagged matter folder (the real-estate "Title" folder is the motivating case; the mechanism is folder-agnostic), driven from apps/worker-connectors, feeding an email to the lawyer who flagged it. NetDocuments publishes no webhook (REST spec §3.5), so a watch is a poll plus a snapshot, not an event.

Both cross the two lines the federation spec §9 drew on purpose: any write scope and background use of a lawyer's token. Those lines were consent decisions. §8 is the section kwiss takes to HSE; nothing in §3–§7 is built before that conversation closes.

1. What exists today (verified against main @ 0035bfb0, re-verified in two review rounds)

2. Design in five lines

  1. The agent never names a NetDocuments folder. It names a destination the lawyer registered; the write boundary re-resolves that destination live, under the lawyer's own token, and refuses if it is not what the lawyer confirmed.
  2. Every filing pauses on a card built from a persisted binding — folder, filename, size, source and how the source came to exist — claims its ledger row in a committed transaction before a single non-retrying upload, and never honours a pre-approval.
  3. Only a lawyer who owns an authorized NetDocuments connection can file or watch, through their own credential and their own registered destinations, after a one-time enrolment recorded server-side.
  4. A watch is a row per (matter, folder), polled on a fixed cadence through a connectors-api read proxy so the bearer never reaches the worker, diffed against a per-item snapshot, owner-isolated end to end, and paused the moment the credential, the membership or the matter access behind it changes.
  5. Everything the design cannot prove about the provider is a probe in §9, and no lane starts before the probe that gates it.

3. Filing back

3.1 Registered destinations, and who may register one

New table user_netdocs_filing_target (organization RLS in the shape of user_drafts_folder, with an owner predicate in every query on both roles — a matter_access-shaped policy would be invisible to the credential boundary, whose transaction sets only the organization): id, organization_id, user_id, connector_id, connector_generation (integer, copied from the connector row at registration), provider_account_id (from the identity read), container_id, container_kind (folder | workspace), container_name, container_parent_id, container_path_recorded (best-effort ancestry at registration, with path_recorded_at), label, matter_id (nullable), status (valid | invalidated), invalidated_reason (reauth | transfer | provider | user), validated_at, created_at.

Freshness. The registration read returns the container's children, not its ancestry (A-22), so the recorded path is a snapshot with a timestamp, not a live fact. W-8 probes whether a verified ancestry operation exists. Until it does, the card names the folder by its live name and id and shows the recorded path with its date, and the write boundary compares the live container_id, container_kind, container_name and container_parent_id against the binding; a mismatch refuses. If W-8 finds an ancestry call, the full path is re-resolved at both points and the recorded path disappears.

Invalidation. A same-owner reconnect keeps the row and the owner, so neither identifies a new consent: the connector row gains a consent_generation counter incremented on every successful token replacement or reconnect completion, and every target and watch carries the generation it was registered under. A custody completion or severance invalidates targets and pauses watches inside its own transaction, in connectors-api where custody actually changes, not in the web action that may be abandoned.

Who may register: an actor for whom resolveNetDocsAccess returns credentialOwnerUserId === actorUserId with status authorized, who has an enrolment row for the current terms version (§4.4). That is the rule. Whether anyone besides the firm-row owner satisfies it at HSE today is a data question the plan answers with a read-only query before §8 goes out, because grandfathered owner-reach rows are permitted by the firm-wide ruling. Everyone else gets owner_credential_required: "North can file into NetDocuments only through a connection in your own name."

Registration is on the connections page: paste a folder URL or id; connectors-api probes it under the actor's token (GET /v2/container/{id}, first page), records the identity fields above, refuses ids that resolve to nothing or to a document. Cap 25 targets per user. Lane L5's picker can feed the same form later.

3.2 The tool: netdocs_file_document

One implementation in packages/agent-runtime/src/tools/netdocs-file.ts, exposed as netdocs_file_document in product chat and north__netdocs_file_document on MCP. A read sibling netdocs_filing_targets / north__netdocs_filing_targets lists the caller's valid destinations by id and label on both surfaces — the model cannot otherwise learn the opaque id behind a label, and MCP descriptions are static.

Arguments: target_id, source ({ kind: "case_document", id }), filename (allow-list sanitizer + NFC, extension must match the source's). Nothing else. Out of scope in v1: mail attachments, new versions of existing NetDocuments documents, moves, renames, deletes, profile attributes.

  1. Credential and enrolment. resolveNetDocsAccess; require credentialOwnerUserId === actorUserId, status authorized, and a current enrolment row; else owner_credential_required or enrolment_required, and no token is minted.

  2. Source authorization is reachability, and origin is part of the answer. Product chat: resolveSourceBytes(organizationId, currentThreadId, id). MCP (no thread): the row must be scope='thread' on a thread the actor owns. The row's origin travels into the binding: an agent-written row (origin != 'upload') is one the product deliberately keeps in the lawyer's personal space and refuses to promote to matter scope, so filing it is the first path by which it becomes firm-visible. The card and the elicitation say so in words. Compute the digest and length from the bytes; cap 15 MB.

  3. Target and binding. Load the target (owner predicate, status='valid', connector and generation still current), re-resolve the container live, and build the binding: { authoringIdentity, actorUserId, targetId, connectorId, connectorGeneration, providerAccountId, containerId, containerKind, containerName, containerParentId, pathShown, sourceId, sourceOrigin, sourceSha256, filename, bytes }. binding_sha256 is the digest of that object.

  4. Confirm against the binding. Product chat: interrupt({ kind: "netdocs_file_confirm", toolCallId, binding }), a strict kind (a new ServerReviewBinding variant, so it cannot fall through as non_review); resume "yes" | "no" for the pending toolCallId only. Any thread participant may answer, by design: the tool then re-executes under the resumer, so step 1 re-runs with their credential, step 3 with their targets, and a colleague without their own connection simply gets owner_credential_required. The binding is rebuilt on resume and compared field by field with the persisted one; any difference refuses. confirm_preapproved is not read by this tool — the existing pin test already fails any file that reads the literal outside the two allowed ones, and WORKFLOW_TOOL_MAP is a closed record, so absence there is a type error. MCP: consent.prepare resolves the same binding under the actor's token and returns it for verbatim rendering plus a closed audit payload (§3.3); decline never reaches the handler.

  5. Claim, upload once, settle. POST /connectors/netdocs-mcp/file with { organizationId, userId, connectorId, invocationKey, binding, contentBase64 }. The boundary, as sync_role: decodes the bytes once, recomputes digest and length and compares them with the binding (mismatch ⇒ 409, no token), re-resolves the target by id and owner predicate and the live container, then:

    • transaction 1 inserts the ledger claim (outcome='pending', claimed_at, the binding digest and the immutable payload fields) and commits. A key that already exists with the same payload returns the stored row (filed, failed, unknown, or pending ⇒ the in-flight receipt); a key that exists with a different payload is refused (409), never returned.
    • the upload runs outside any database transaction, exactly once, with no retry.
    • transaction 2 settles filed with the document id and URL, or failed when the request provably never left, or unknown when the outcome is not knowable.
    • a sweep settles pending rows past a lease (15 minutes) to unknown, because process death runs no code.

    invocationKey is sha256(organization_id, user_id, "chat", thread_id, tool_call_id, binding_sha256) in product chat, and sha256(organization_id, user_id, "mcp", binding_sha256) on MCP, where the host may retry an accepted elicitation and prepare runs once per call: a retry then collides with the existing claim instead of uploading twice.

  6. Receipt carries the document id, the URL, the destination as confirmed, the byte count, and what North did not do. An in-flight claim returns "a filing for this confirmation is already running"; an unknown says the upload may have landed and gives the folder link. A no writes no ledger row.

The REST seam. PR #216's packages/connector-netdocuments is extracted as the single closed REST client for the whole repo; this spec adds the probed upload primitive and the identity read to it. Its allow-list guard test lives in that package and names every permitted call shape (identity, container listing, upload, plus the watch listing of §4.2 which is the same call); a shape not in the list fails. connectors-api is the only process that constructs it with a bearer. packages/connector-netdocs-mcp stays search + fetch; apps/mcp-server/src/mcp-app.ts is untouched.

3.3 Audit, everywhere the write can be seen

3.4 Where the tool is admitted, and where it hides

3.5 Blast radius of a wrong write, stated

A wrong filing is a document reachable from the lawyer's thread, uploaded under their NetDocuments identity into a folder they registered, named as the agent proposed and they confirmed.

4. Folder watch

4.1 Model

matter_netdocs_watch (organization RLS + owner predicate): id, organization_id, matter_id (composite FK), owner_user_id, connector_id, connector_generation, provider_account_id, expected_access_scope, container_id, container_name, container_parent_id, container_path_recorded, label, consented_at NOT NULL, terms_version, status CHECK (active | paused), paused_reason CHECK (null | reauth | transfer | user | error | lockout | too_large | access_lost), next_poll_at, last_polled_at, last_full_listing_at, coverage CHECK (full | partial | unknown), consecutive_failures, last_error_class, created_at. UNIQUE (matter_id, container_id, owner_user_id).

netdocs_watch_item (same posture), the snapshot: watch_id, nd_document_id, name, extension, modified, nd_url, first_seen_at, last_seen_at; PK (watch_id, nd_document_id).

netdocs_watch_event (same posture): id, organization_id, watch_id, owner_user_id, kind CHECK (new | updated | paused | resumed | too_large), nd_document_id, name, nd_url, modified, paused_reason, seen_at, delivery_status CHECK (pending | sending | sent | failed), delivery_attempts, delivery_claimed_at, sent_at. UNIQUE (watch_id, nd_document_id, modified) for document kinds. Every email corresponds to an event kind, and every event to one atomic transition.

The watch is personal in v1: its owner's credential, its owner's reading, its owner's inbox. Direct children only, no subfolders, stated in the copy.

4.2 The poller: netdocs-watch-poller in apps/worker-connectors

Redis-mutexed, best-effort, never fatal, tick every 5 minutes; cross-tenant scan under scheduler_role of watches due by next_poll_at whose state is active, or paused with reason reauth or lockout (both recover; transfer, user, too_large and access_lost do not). Per watch, mutations run under withRlsTransaction(..., { userId: owner_user_id }) on the app-role pool so the owner predicate resolves; scheduler_role holds the scan grant only.

  1. Revalidate before anything else: the connector is authorized, still owned by owner_user_id, with the recorded consent_generation and expected_access_scope; the owner is a current, non-banned member of the organization; the owner still has access to the linked matter. Any failure pauses with the matching reason, emits one paused event and stops — the newsletter loop rechecks banned recipients the same way before sending.
  2. List through the credential boundary, never with a bearer in the worker. POST /connectors/netdocs-mcp/watch/list takes { organizationId, watchId, skipToken? }, re-derives the watch and its credential server-side, mints internally, performs exactly the approved listing call from the shared client's allow-list, and returns items plus the next token. The worker holds the connectors-api internal token only, which by itself mints nothing (the route accepts a watch id, not a user).
  3. Diff against the snapshot, full listings only: an empty snapshot is the first listing (insert everything, emit nothing); an id absent from the snapshot is new; a later modified is updated; equal is nothing. Comparison is per item, so ties lose nothing. Absent ids stay (deletions are invisible to listings) and age out after 30 days only for watches whose coverage has been full since the cut-off, so a long partial or paused stretch cannot silently empty a snapshot and re-announce the whole folder.
  4. Coverage and budget. Up to 3 pages per watch per tick with no ordering available (A-23): a complete listing sets coverage='full'; a truncated or failed one sets partial, writes no snapshot rows and no events, and reschedules. Three consecutive partials pause the watch with reason too_large and one too_large event. The limiter counts undifferentiated members, so a smaller capacity on the same key would starve the poller rather than reserve anything: the proxy route reserves in one script against two keys — a poller key at 6 per minute and the shared key at 18 — so interactive reads keep at least 12 and the poller is never starved below its own reservation. A lockout-shaped response (A-16: errors without a 429) pauses with reason lockout and a persisted backoff of 15 → 30 → 60 minutes, capped at 6 h, in next_poll_at.
  5. Cadence and cap, honestly. At 6 calls per minute reserved, one credential sustains 360 listing calls per hour. A watch costs up to 3 calls per round, so a 15-minute cadence costs 12 calls per watch per hour: 30 watches per credential is the cap, and that is what the consent copy promises. The organization-wide cap is 50 across credentials. The poller records per-watch lag; a watch overdue by more than an hour raises a metric.
  6. The repository audit log is not in v1. It would collapse N listings into one call per credential and add deletions and renames, but its checkpointing, volume handling, naive dates and deletion mapping are unbuilt, and container attribution was never checked. It gets its own design spec after the read-only probe, with its own event kinds and cursor protocol.

The poller reads listings, never content: nothing enters the corpus or any digest.

4.3 The notifier: netdocs-watch-digest in apps/worker-notifications

Every 5 minutes, per watch with pending events: claim under FOR UPDATE SKIP LOCKED (sending, delivery_claimed_at, attempts + 1), re-check the owner's membership and account status inside the claim, send one email through the digest mailer (apps/worker-notifications/src/newsletter/mail.ts, which already takes an idempotency key derived here from the claimed event ids), confirm sent. A crash between send and confirm is reclaimed after 15 minutes and resent under the same key; after 5 attempts the batch settles failed. The dev outbox counts as sent; an unconfigured mailer claims nothing.

Content: matter display number and name, watch label, up to 10 documents with their modified and the stored nd_url, "and N more"; paused, resumed and too_large each produce their own short email naming the reason. Retention and recipients are §6's matrix and §8's disclosure. The snapshot sweep runs in the poller, not the mail loop, so an unconfigured mailer cannot skip it.

In-product: the matter page shows a watch and its last 20 events to its owner only. Briefing, glance and desktop toasts are out of v1.

4.4 Enrolment, lifecycle backend, consent stamp and controls

Enrolment is a row, not a click: user_netdocs_enrolment (organization_id, user_id, terms_version, accepted_at), checked server-side at target registration, watch registration, the filing claim and the watch proxy. A UI acknowledgement alone is bypassable by a direct tool or route call. Raising the terms version re-asks.

Lifecycle backend: register a watch by URL or id (container probe under the owner's token, identity fields recorded, consent stamp, caps), pause, resume, delete. Two hooks live where custody actually changes, inside its transaction: a token replacement or reconnect completion invalidates that connector's targets and pauses its watches (reauth for the same owner, transfer for a new one); a disconnect or severance pauses with reason user. Web actions call these; they never own them, because a web action can be abandoned while custody has already changed.

Consent copy on the per-watch toggle: "North will check this folder every 15 minutes using your NetDocuments access, including when you are not using North. It reads the folder listing only, never document contents, and does not see subfolders. New and changed documents are emailed to you. You can pause or delete it here at any time." The enrolment screen carries the full §8 facts.

4.5 What the watch never does

Never polls with a credential its owner did not personally connect. Never puts a DMS bearer in the worker. Never reads content. Never writes to NetDocuments. Never notifies anyone but the owner, and never after they lose membership, standing or matter access. Never keeps polling a connection that changed hands or left authorized. Never announces a folder's history, and never emits from a partial listing.

5. Surfaces and copy

6. Security posture

7. Acceptance

  1. Registration: an eligible owner registers by URL; a member reading through the firm row sees the hint and no form; an actor without a current enrolment row is refused server-side, not only in the UI; the 26th target is refused.
  2. Product chat: the card shows the live folder identity, the source origin and the filename; "no" writes no ledger row; "yes" files once; a resume for another toolCallId is rejected; a resume by a different participant re-executes under them and, without their own connection, returns owner_credential_required and mints nothing; a target invalidated between card and "yes" refuses; a folder renamed or re-parented between card and "yes" refuses.
  3. Claim: two concurrent executions of one toolCallId produce one claim and one upload (integration test against a real Postgres UNIQUE); a second call with the same key and a different payload is refused with 409; an MCP host retrying an accepted elicitation collides with the existing claim and uploads once.
  4. Durability: a claim committed and then abandoned (no settlement) is swept to unknown after the lease, and a caller meeting the in-flight claim gets the in-flight receipt, not a second upload.
  5. Boundary: bytes whose digest or length differ from the binding are refused before any token is minted.
  6. MCP: the targets tool lists destinations; the elicitation renders the binding; decline ⇒ no handler call; accept ⇒ one upload, one audit row carrying the resolved destination and byte count from the prepared payload, one ledger row, one product event.
  7. Hiding: the write tool is absent on both surfaces when the feeder is disconnected, when the principal is outside the pilot, and without mcp:write; back on reconnect; the three named mutations each fail a test.
  8. REST allow-list: a call shape outside the list fails the guard in the shared package, and no other package constructs the client with a bearer.
  9. Exclusions: no filing tool in WORKFLOW_TOOL_MAP (type) or in the automation, replay and desktop toolbelts (test); the existing pin test still fails any new reader of confirm_preapproved.
  10. Watch: the first full listing emits nothing; an added document produces one new event and one email within two poller ticks and one notifier tick; an edit produces updated; a partial listing writes nothing; three partials pause with too_large and one email; a lockout persists a backoff that survives a worker restart and later recovers; requires_reauth, a same-owner reconnect (generation bump), a transfer, a disconnect, a lost membership and a lost matter access each pause with the right reason and the right one-time email; recovery emits resumed.
  11. Budget: with the two-key reservation, 30 watches on one credential each poll within 15 minutes while interactive reads continue; the poller is not starved by interactive traffic; lag is exported.
  12. Isolation: a second user in the same org reads no watch, item, event or filing row through the app role; the matter page renders the section only for the owner; the watch proxy refuses a watch id that is not the caller's worker-scoped route contract.
  13. Delivery: the same event batch is never sent twice across a crash between send and confirm (idempotency key asserted on the mailer); a banned or removed owner is not emailed.

8. The consent point with the firm (for kwiss to raise with HSE)

Three asks, in the order they change the design, then the facts to state. Nothing in §3–§4 is built before the first two are answered; the phase-0 write probe is itself an instance of ask 1.

  1. North writes into the firm's DMS under lawyers' identities. The grant already includes edit; what changes is that North uses it, one document per confirmed filing, never a replace, move or delete. What the DMS audit trail records about the actor and the application is captured by the probe before this is described to HSE as fact. North cannot undo a filing. Accept for the partner first, on a designated scratch folder, then in production?
  2. A lawyer's NetDocuments access used in the background. A watch lists a folder every 15 minutes with the lawyer's own token, nights and weekends included, and those reads appear in NetDocuments' logs as theirs. The lawyer enrols once and consents per watch. Alternative: a NetDocuments service user for watches (no client-credentials grant exists, A-19, so it is a designated user who consents once). It removes the personal-token question and inherits one identity's ACL for every watch, the ethical-wall concern that killed it for ingestion.
  3. Who may write or watch. The rule is: a lawyer who owns an authorized NetDocuments connection. The firm-wide connection is read-only for everyone but its owner, and the exclusivity ruling forbids new personal connections while it is live, though existing owner-reach rows are grandfathered. The plan verifies how many such rows HSE holds before this question goes out, so the ask is exact: either it is the partner alone, or it is the partner plus N grandfathered lawyers. If HSE wants more, the ruling is reversed by its own lane.

Facts to state alongside: the consent screen is NetDocuments' own app and does not change; North's connect page and a one-time enrolment acknowledgement do; document names from watched folders leave the DMS by email through North's transactional email processor (retention and data-processing posture named in the ask) and are kept 90 days in North, with the retention matrix of §6 for everything else; a watch is incomplete by nature (no deletions, no subfolders, no renames, promotions unverified, partial listings skipped); the poller shares the credential's rate budget, so with the firm-wide credential a lockout affects everyone reading through it; an admin reconnect changes custody and pauses the previous owner's watches; the blast radius is §3.5 verbatim, including the agent-written-source case.

9. Assumption ledger

# Assumption Confidence What falsifies it, and the seam
W-1 A document is created with one multipart POST to the document family carrying the destination container id, name and extension, returning the new id Unknown, probe-gated (no upload row in the endpoint map; every inferred sub-path was contradicted live) Phase-0 probe; seam: the shared client's upload primitive and its allow-list
W-2 A profile attribute (description) can be set on upload Unknown Same probe; matters only if HSE wants a North marker
W-3 The edit grant covers creation in a folder the user can write to Unknown (token scope is null, A-5/A-6) 403 on the probe ⇒ a re-consent lane before phase 1
W-4 A name collision creates a second document rather than overwriting Inferred (ids are the identity; nothing documents upload collisions) Probe uploads the same name twice and reads both ids
W-5a The identity read returns a user id Verified live (A-5)
W-5b That id is stable across reconnects and usable as provider_account_id Unknown Probe: re-consent in the scratch account and compare the id; if it moves, targets bind to the connector generation alone
W-6 No upload idempotency contract exists (no key header, no dedupe) Unknown, probe-gated Probe: repeat one multipart body verbatim and look for a documented key header in the response and error shapes; two documents ⇒ confirmed absent
W-7 A crash after the upload request cannot be reconciled automatically Accepted by design If the probe shows a new document is searchable by name within seconds, a reconciliation read can settle unknown later
W-8 A verified operation returns a container's ancestry (cabinet › workspace › folder) Unknown (the container GET returns children, A-22) Read-only probe; until then the card shows the live folder identity plus a dated recorded path
P-1 Container listings can be ordered Contradicted (A-23) Design assumes none
P-2 skiptoken is a stable cursor under concurrent mutation Unknown (A-18) Read-only probe with a concurrent edit; if unstable, one page per tick and a smaller folder cap
P-3 Modified advances on every user-visible change Contradicted for official-version promotion (A-25) Accepted; the copy says "new and changed documents"
P-4 A two-key reservation of 6 per minute for the poller leaves interactive reads unaffected Inferred from the limiter code and A-16 Lockout or measured interactive denial during the pilot ⇒ lower the reservation and the watch cap together
P-5 The container listing returns direct children only Inferred (A-8/A-22 describe the envelope, not the recursion) Read-only probe on a folder containing a subfolder with documents
P-6 The repository audit log attributes activities to containers Unknown (A-13 verified the call, not the fields) Read-only probe; decides whether the log spec is worth writing
P-7 The digest mailer's idempotency key prevents duplicate sends Verified in repo (in production use by the weekly digest)
C-1a HSE accepts filing under lawyers' identities (§8.1) Unknown No ⇒ phase 1 does not start; the watch may still proceed on C-1b
C-1b HSE accepts background use of a lawyer's token (§8.2) Unknown No ⇒ phase 2 does not start, or becomes the service-user variant with its own spec

10. Not in scope

Browse and ids (lane L5); mail attachments; new versions of existing NetDocuments documents; moves, renames, deletes, profile attributes; firm-shared watches; subfolders; briefing, glance and desktop surfaces; the repository-audit-log feed (its own spec after the probe); the exclusivity-ruling reversal (its own lane if §8.3 asks for it); ingestion of anything a watch sees; any change to apps/mcp-server/src/mcp-app.ts.