oficina / docs Public

Next review slice: anchored comment threads and shared Estilo presentation open

Proposal for the next Docs vertical slice: named collaborators can discuss selected text, reply, resolve or reopen a thread, and keep the conversation when its target disappears. This is a design candidate for Claude Fable review, not an approved implementation plan. No implementation has begun.

Evidence read after fetch on 2026-09-07:

  • Docs origin/main 499d200: reader/writer grants in internal/docs/grants.go; public links are read-only; no comment, suggestion or named-version store/routes exist. Existing automatic snapshots are replay accelerators and snapshots.go deletes superseded snapshots. They cannot be repurposed as user-visible history by adding a list page.
  • Docs origin/docs-finish 21be947 and discussion #35 contain link authoring, plain-text extraction corrections and other editor work absent from main. Import origin/worktree-import-documents e64fcc7 / #20 contains the Rust builder, not a finished importer. Both owner branches stay untouched. Do not replace main with either branch: they predate sharing.
  • Docs design sections 7.4 and 10.4 define relative-position anchors, preserved detached threads, scriptless block anchoring and Docs ownership of the Estilo thread/panel primitives. Section 14 defers suggestions until comments exist and the version timeline until named versions are in use; those require separate reviewed extensions.
  • Sheets origin/main 4aea8f4 has real encrypted comments and cell history/restore. comments.go and templates/comments.html are the concrete second consumer. Its cell anchors are stable row/column IDs; its renderer currently skips deleted targets (comments.go:125-131), which must be corrected during adoption rather than copied into the shared primitive.
  • Estilo has no main branch. Its fetched baseline is origin/start ad0edf4, theme-only. The root agent separately owns feat/shared-save-status. Do not conflict with that work.
  • Suite authority remains the citation: suite origin/main 60b01a8, docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md, ruling 15. This proposal does not change it.

Smallest useful boundary:

  1. Add a named commenter grant between reader and writer. Readers can read review conversations; commenters, writers and owners can create/reply/resolve. CanWrite remains writer-only. Public capability pages gain no comment bodies or authors in this slice; anonymous holders cannot post, and named access still comes through the existing sign-in/grant flow.
  2. Add separate thread and message rows. A thread has an opaque ID, document ID, sealed anchor payload, actor/time, resolved actor/time and a revision. Messages have opaque IDs, thread ID, sealed body, named author and timestamp. A client-generated request ID deduplicates retries per actor/document. Bodies and quoted excerpts are sealed before database writes; the content-sealing branch must be activated before this feature is enabled. Do not store quotes in logs or plaintext convenience columns.
  3. Add Store operations and POST/GET routes for list, create, reply, resolve and reopen. All reads pass GetFor; every mutation checks the commenter predicate against the document and thread inside the same write transaction. Recheck grant revocation there. A stale resolve carries the thread revision and gets a conflict instead of silently resolving a newer reply. A reply reopens a resolved thread in that same transaction. Keep message text when validation or connectivity fails.
  4. Limit the first thread body to 2,000 runes, matching Sheets' existing scale, but reject overlong bodies rather than silently truncating them. Cap encoded anchors and quoted context separately. Page thread/message lists; never load an unbounded discussion into one response. These proposed limits need Fable review and mechanical tests.

Anchoring is part of this slice, not a browser-only promise:

  • Store Yjs relative start/end positions plus a bounded last-known excerpt and observed document sequence in the sealed anchor payload. Use the pinned y-prosemirror mapping functions, not numeric ProseMirror offsets or a text search.
  • The server resolves anchors against authoritative replayed content through a small Rust/wasm API. yrs 0.23.5 already supplies StickyIndex decoding and get_offset. A malformed, foreign or missing target cannot be treated as a live selection. Resolve to attached or detached without deleting the thread. Detachment is derived from current document state; do not mutate storage during GET merely to record it.
  • Generate block anchors on the server for the scriptless form. A block's index in a rendered list is not its durable identity. The scriptless route can create a block-anchored thread, reply and resolve using ordinary forms.
  • Export additions bump the existing core ABI according to the current exact-match rule. This invalidates accelerator snapshots under the existing policy; it does not alter historical data. Do not silently split the snapshot encoding version while import #20 has that question open. Include the long-log replay checks in the gate.
  • The save queue currently exposes flush but no explicit acknowledged-prefix contract. Do not assume await flush means an anchor is durable. Creating a comment on unsaved text requires either a bounded acknowledged save barrier or a server response that preserves the draft until the referenced content has committed. The reviewed spec must choose that behavior explicitly.

Estilo adoption:

  • Build the thread presentation and a responsive review panel in Estilo from Docs' actual implementation, then make Sheets' existing comments page the second real adopter before calling the interface settled.
  • Keep document-relative anchors, cell IDs, permissions, route URLs and persistence in the apps. Estilo owns escaped message/author rendering, accessible thread structure, status and action layout, focus behavior and reflow. It must support detached threads without discarding their content. Keep the day theme unchanged.
  • Use estilo-* attributes, 24px targets, 320px reflow and complete keyboard behavior. Do not add role=dialog or role=toolbar without the corresponding interaction model. Standalone comment pages/forms work without scripts; selected-range creation/highlighting is the scripted enhancement.
  • Coordinate on the existing shared-save-status thread for a save barrier if that interface needs extending. This plan does not take over the root agent's branch.

Copy needed before UI files are written: comment navigation/title; commenter role; body label and submit; reply; resolve/reopen; resolved/detached status; open/resolved filters; no-thread state; block selector; overlong/empty body error; stale-thread conflict; save-before-comment status; preserved-draft retry guidance. Reuse approved common button labels only where they mean exactly the same thing. The messages in Sheets are English literals today, so adoption includes catalog keys and twelve locales rather than copying those strings into Estilo. Root coordinates the required copy review.

Release evidence: two named browser sessions create and reply to an anchored thread; insertion before the selection preserves its target; deletion detaches the thread and preserves every message; a revoked commenter cannot post or resolve; a reader cannot mutate; public-link responses contain neither comments nor identities; idempotent retries create one message; stale resolution preserves newer discussion; scriptless block comments work; body/anchor markers are absent from DB and uncheckpointed WAL; both Docs and Sheets render the shared component with keyboard and 320px checks. Mutate the permission, anchor and preservation checks and observe the correct failures. Spec review, whole-branch Fable review and green remote CI precede merge/deployment.

Later slices remain separate: mentions/notifications after the suite delivery contract is real; suggestions with accept/reject after comments; named immutable checkpoints before any timeline. Restoring a named version must create a new authored change or an explicit restored copy; applying an old Yjs snapshot to a live document is a merge, not a rewind.

Sheets worker confirmed no active comment-renderer work. Their workbook-tabs draft ee62d39 / oficina/sheets discussion #18 will make comment routes explicitly tab-scoped and preserve the selected tab through add/resolve. Adopt that route shape after it lands. They also clarified that stable-axis deletion is not implemented yet, so the existing skip-on-missing-target branch matters for corruption and future structural edits, not an existing user-facing delete action. Keep scriptless add/resolve and escaped content during Estilo adoption.