WIP: docs: plan sharing and app-password slice 1 #823

Closed
kayg wants to merge 1 commit from docs/sharing-slice-1 into dev
Owner

Plan only: docs/plans/0409-sharing-slice-1.md; settled decisions: docs/grills/sharing.md.

Revision after final independent review comment #72378:

  • Added one durable per-note application fence. Devices drop entries at/below their fence; purge advances it to R; re-grant installs a full snapshot at G and advances it to G. The server applies the committed fence to journal, room, history and content responses, regardless of a supplied cursor.
  • Corrected Edit-to-Read recovery: retain readable content and rescue only edits refused after the downgrade. Purge shared copies only after loss of Read.
  • Added delayed-delivery-after-purge and delayed-delivery-after-re-grant barriers, direct-request refusal, read/room/fan-out cases and randomized interleavings.
  • Split implementation into three dependent PRs: scoped auth/admission; shared data/recovery/search; approved UI/end-to-end checks. Changed the gate section to final slice acceptance plus per-PR subsets.

The remaining grill is whether app passwords may carry the independent notes.share scope (recommended yes) and whether a Read grant to a shared View includes history (recommended yes in Slice 2). Documentation only; rtk git diff --check passed and no implementation tests were run.

Plan only: [docs/plans/0409-sharing-slice-1.md](docs/plans/0409-sharing-slice-1.md); settled decisions: [docs/grills/sharing.md](docs/grills/sharing.md). Revision after final independent review comment #72378: - Added one durable per-note application fence. Devices drop entries at/below their fence; purge advances it to R; re-grant installs a full snapshot at G and advances it to G. The server applies the committed fence to journal, room, history and content responses, regardless of a supplied cursor. - Corrected Edit-to-Read recovery: retain readable content and rescue only edits refused after the downgrade. Purge shared copies only after loss of Read. - Added delayed-delivery-after-purge and delayed-delivery-after-re-grant barriers, direct-request refusal, read/room/fan-out cases and randomized interleavings. - Split implementation into three dependent PRs: scoped auth/admission; shared data/recovery/search; approved UI/end-to-end checks. Changed the gate section to final slice acceptance plus per-PR subsets. The remaining grill is whether app passwords may carry the independent `notes.share` scope (recommended yes) and whether a Read grant to a shared View includes history (recommended yes in Slice 2). Documentation only; `rtk git diff --check` passed and no implementation tests were run.
docs: plan sharing slice one
Some checks failed
CI / server (pull_request) Has been cancelled
CI / rust (pull_request) Has been cancelled
CI / web (pull_request) Has been cancelled
10ca6178df
kayg changed title from docs: plan sharing and app-password slice 1 to WIP: docs: plan sharing and app-password slice 1 2026-11-01 06:29:32 +00:00
Author
Owner

Independent plan review

verdict: revise. reviewed docs/plans/0409-sharing-slice-1.md at 33473d2033, against the listed ADRs, the checked-in sharing grill and the settled decisions supplied in the review brief. this is a plan review, not a claim that the proposed implementation already has these defects. no builds, tests, commits or pushes were performed.

contradictions and reopened decisions

  • Q10, file edits versus personal organization: closing grill 3 re-asks what Edit means and omits tags and cover from its proposed answer. the data-model paragraph at L68 makes move owner-only, while the access table at L87 groups pin with shared-file edits requiring edit or owner. distinguish shared text/tags/cover from each person's pinning and placing. apply that distinction to Task 3 and its action matrix; do not resolve it with another product question.
  • Q16a–d, revoked offline edits: closing grill 5 re-asks an answered question. the failure-table row at L120 says to retain an unsent local edit for “explicit recovery”, rather than the decided private “Your edits to ‹title›” note containing the recipient's own changed blocks plus title. migration/recovery L113 mentions removing the list item and index only; Task 3 L187, Task 4 L196, Task 5 L207, the revoked-state mockups and gates 2/5 never specify the rescue-and-purge workflow on every device's next sync. add durable, retry-safe rescue before deleting the shared copy, including local bodies, attachments/cache and pending operations, without retaining other people's unchanged blocks. link guests have no offline editing; links can stay deferred, but record this constraint rather than reopening it later.
  • Q14, owner deletes: the goal at L5 says Claude owns the note, then kayg can “revoke access”, although the decisions, data model and grant-management table make grant changes owner-only. describe owner revocation explicitly; any recipient “remove from my list” must be a separate personal operation. owner-only deletion itself agrees with Q14 and must remain settled, including the resulting recipient tombstone and purge. grill 3 must not re-ask that boundary.
  • Q11, search: Task 4's shared search is consistent with the supplied settled requirement and ADR 0031. keep it in this useful slice. however, SH5's manage shares from the search palette has no explicit action registration/UI/gate in Tasks 2/5; add it rather than treating palette management as undecided.
  • ADR 0060: closing grill 1 is deployment setup, not an unresolved sharing policy. use the existing visibility rule and record shared-group membership as a prerequisite for these two accounts.
  • source completeness: at this head, docs/grills/sharing.md ends at Q8; current remote dev has the same content. neither it nor #409's available comments contains A1–A13, G1–G7 or Q10–Q16. the supplied Q10/Q11/Q14/Q16 decisions are authoritative for this review. i cannot honestly attest to every A/G item without their text. reconcile the full decided record and add an item-to-plan/gate map before approval. closing grill 2 (app-password share authority) and 4 (read history) have no settled source in the available material: cite the existing answer if covered by A/G; do not ask kayg again or present a recommendation as an approved decision.

findings

P1 · access coverage is broad, but enforcement is still a collection of call-site promises (L80–96; Tasks 1/3).

the table covers reads, lists/metadata/snippets, backlinks/Linked here, attachments/thumbnails, history/restore previews, search results/counts/highlights/recents, sync/journal replay, rooms, websocket delivery and DAV scope separation. this is a good inventory, but a shared helper is not yet one mandatory choke point. export and notifications are absent; preview images are mentioned only through thumbnails/backdrops, without cache/delivery rules. explicitly cover download/archive/export jobs, image variants and private response caches, attachment ranges/resumed reads, and any notification or webhook payload. declare unavailable paths out of scope with a refusal test instead of silently omitting them. preserve SH8: returning a readable source's backlinks must not reveal an unreadable referring note.

specify one authorization contract combining credential capability and current object rights, enforced in the executor/resource resolver, with direct byte-serving adapters required to use an authorized resource handle. centralize list/search visibility predicates before counts/snippets and paginate after filtering. require an enumerated route/action matrix that fails for unclassified additions. ADR 0011 calls for one set of rules, not independent policy implementations in every adapter.

P1 · revocation has no defined ordering against the owner's durable write or buffered delivery (L68, L92–93, L113, L120).

grants commit in shared auth.db; rooms, owner files/history and owner journals commit elsewhere. “recheck inside the serialized write transaction” does not identify a transaction/lock shared with revoke. revoke can commit after a grant check but before file/history commit; a response or room update can already be queued. specify a common per-resource serialization boundary or equivalent grant-generation validation spanning authorization and durable commit, plus publication ordering. define the boundary precisely: bytes already delivered cannot be recalled. test revoke between check/commit and check/enqueue, pending room flushes with multiple authors, edit-to-read downgrade, lost replies/idempotent retry, and owner deletion. do not discard another author's accepted pending edits when one recipient loses access.

P1 · filtering journal replay by current grants can suppress the very purge event a revoked device needs (L91, L113; Task 3).

after the final grant disappears, a uniform “current access” filter rejects sharing.revoked too. define recipient-addressed control tombstones that remain replayable without content rights, with sufficient stable identity to purge/rescue but no note body. establish monotonic grant generations/source versions so delayed granted/changed events cannot resurrect a revoked copy after revoke/regrant. define snapshot/reset recovery for a cursor older than journal retention and atomic application with the client cursor. gates must include two offline devices, crash during rescue/purge and replay after retention, not only one live recipient seeing one event. this is required to fulfill Q16, ADRs 0044 and 0047.

P1 · app-password scope intent is sound; the end-to-end enforcement contract is incomplete (L75; Task 1).

the plan explicitly refuses login, setup/recovery, passkeys, account changes and unrelated DAV protocols, and reserves grants for notes.share. however, it accepts credentials “only on notes routes” while mutations actually use the generic /api/v1/actions/{name}. at this head, Authenticator::Identity returns only user/Installation and api::Signed creates a Caller with no credential kind/scopes; run_action dispatches arbitrary registered names, and journal reads receive only the user. Task 1 must name the identity/Caller/executor changes that preserve and enforce the scope ceiling on every dispatched action and sync operation, not merely a route check. specify whether scopes imply one another, how sharing.list/shared-with-me/search are classified, and how mixed sync or bulk payloads are checked per operation. an owner user id must never bypass the Installation's scope ceiling. a notes.write password must fail grant/revoke even when its user owns the note; notes.share still cannot reshare somebody else's note. require a deny-by-default capability map and tests through the generic endpoint, cached idempotent outcomes, credential revocation and an established websocket. there is no demonstrated bypass here yet; this missing contract prevents approval.

P2 · fan-out is O(R) events plus recipient indexing work, with unbounded global write pressure (L64, L100–104).

batching delivery at 100 rows does not bound the preceding transaction that enumerates all recipients, queued bytes, oldest-event latency or work across all owners. shared FTS duplicates O(R × B) text overall; updating it can dominate compact journal events. define a slice-1 recipient limit, bounded enumeration/queueing, fairness, backlog limits, metrics and restart behavior. keep owner edits independent of recipient indexing. spell out grant-time snapshot plus journal watermark and deduplication so a share created while the worker is behind neither misses an edit nor replays arbitrary earlier events. define per-recipient order and what happens if the source journal is compacted before the fan-out cursor catches up. measure owner edit latency and recipient catch-up at the supported recipient limit.

P2 · search correctness needs bounded candidate refill and full-pipeline isolation (L94, L105; Task 4).

K returned matches is not the amount of authorization work when many stale candidates are denied. specify capped candidate scanning/refill, ordering and pagination so revoked rows neither hide readable results nor dominate latency. grant checks after matching do not alone address per-index vocabulary/typo suggestions, ranking statistics, previews or cached recents derived from revoked text. extend the stale-index gate to these outputs, and retain ADR 0031's measured first-results budget.

concrete cuts

  • ship named-person, single-note, view/edit with the existing visibility rule. keep Views, links/QR, group audiences and custom names deferred as proposed. use a concrete fixture/deployment prerequisite for Claude/kayg visibility.
  • keep one active named grant per recipient and effective-permission resolution, but defer runnable synthetic link/group overlap machinery and its UI until those audiences exist. retain a schema extension path.
  • separate the app-password Settings UI from the sharing implementation PR if needed; creation/list/revoke already have APIs after #782. keep scoped auth enforcement and one-time secret behavior as prerequisites. use the existing generic action adapter, not a parallel notes protocol.
  • avoid an additional standalone FTS subsystem if the existing SearchProvider/index pipeline can index shared references. keep shared search, history authorship, attachment inheritance and palette share management, which are part of the decided useful flow.
  • cap recipients for this slice and use the existing journal for delivery; do not add future audience features to justify an unrestricted fan-out framework. do not cut authorization coverage, durable revocation tombstones, offline rescue/purge or race gates.

approval requires: reconcile the complete decided record without another grill; make the authorization/scope boundary and revoke ordering concrete; specify durable multi-device rescue/purge; and bound fan-out. no P0 finding is established by this plan-only review.

## Independent plan review **verdict: revise.** reviewed `docs/plans/0409-sharing-slice-1.md` at **33473d2033e2484a03cfcd991f913165cd4dc198**, against the listed ADRs, the checked-in sharing grill and the settled decisions supplied in the review brief. this is a plan review, not a claim that the proposed implementation already has these defects. no builds, tests, commits or pushes were performed. ### contradictions and reopened decisions - **Q10, file edits versus personal organization:** closing grill **3** re-asks what Edit means and omits **tags and cover** from its proposed answer. the data-model paragraph at **L68** makes move owner-only, while the access table at **L87** groups **pin** with shared-file edits requiring edit or owner. distinguish shared text/tags/cover from each person's pinning and placing. apply that distinction to Task 3 and its action matrix; do not resolve it with another product question. - **Q16a–d, revoked offline edits:** closing grill **5** re-asks an answered question. the failure-table row at **L120** says to retain an unsent local edit for “explicit recovery”, rather than the decided private **“Your edits to ‹title›”** note containing the recipient's **own changed blocks plus title**. migration/recovery **L113** mentions removing the list item and index only; Task 3 **L187**, Task 4 **L196**, Task 5 **L207**, the revoked-state mockups and gates **2/5** never specify the rescue-and-purge workflow on **every device's next sync**. add durable, retry-safe rescue before deleting the shared copy, including local bodies, attachments/cache and pending operations, without retaining other people's unchanged blocks. link guests have **no offline editing**; links can stay deferred, but record this constraint rather than reopening it later. - **Q14, owner deletes:** the goal at **L5** says Claude owns the note, then kayg can “revoke access”, although the decisions, data model and grant-management table make grant changes owner-only. describe owner revocation explicitly; any recipient “remove from my list” must be a separate personal operation. owner-only deletion itself agrees with Q14 and must remain settled, including the resulting recipient tombstone and purge. grill **3** must not re-ask that boundary. - **Q11, search:** Task 4's shared search is consistent with the supplied settled requirement and ADR 0031. keep it in this useful slice. however, SH5's **manage shares from the search palette** has no explicit action registration/UI/gate in Tasks 2/5; add it rather than treating palette management as undecided. - **ADR 0060:** closing grill **1** is deployment setup, not an unresolved sharing policy. use the existing visibility rule and record shared-group membership as a prerequisite for these two accounts. - **source completeness:** at this head, `docs/grills/sharing.md` ends at **Q8**; current remote `dev` has the same content. neither it nor #409's available comments contains **A1–A13, G1–G7 or Q10–Q16**. the supplied Q10/Q11/Q14/Q16 decisions are authoritative for this review. i cannot honestly attest to every A/G item without their text. reconcile the full decided record and add an item-to-plan/gate map before approval. closing grill **2** (app-password share authority) and **4** (read history) have no settled source in the available material: cite the existing answer if covered by A/G; do not ask kayg again or present a recommendation as an approved decision. ### findings **P1 · access coverage is broad, but enforcement is still a collection of call-site promises (L80–96; Tasks 1/3).** the table covers reads, lists/metadata/snippets, backlinks/Linked here, attachments/thumbnails, history/restore previews, search results/counts/highlights/recents, sync/journal replay, rooms, websocket delivery and DAV scope separation. this is a good inventory, but a shared helper is not yet one mandatory choke point. **export and notifications** are absent; preview images are mentioned only through thumbnails/backdrops, without cache/delivery rules. explicitly cover download/archive/export jobs, image variants and private response caches, attachment ranges/resumed reads, and any notification or webhook payload. declare unavailable paths out of scope with a refusal test instead of silently omitting them. preserve SH8: returning a readable source's backlinks must not reveal an unreadable referring note. specify one authorization contract combining credential capability and current object rights, enforced in the executor/resource resolver, with direct byte-serving adapters required to use an authorized resource handle. centralize list/search visibility predicates before counts/snippets and paginate after filtering. require an enumerated route/action matrix that fails for unclassified additions. ADR 0011 calls for one set of rules, not independent policy implementations in every adapter. **P1 · revocation has no defined ordering against the owner's durable write or buffered delivery (L68, L92–93, L113, L120).** grants commit in shared `auth.db`; rooms, owner files/history and owner journals commit elsewhere. “recheck inside the serialized write transaction” does not identify a transaction/lock shared with revoke. revoke can commit after a grant check but before file/history commit; a response or room update can already be queued. specify a common per-resource serialization boundary or equivalent grant-generation validation spanning authorization and durable commit, plus publication ordering. define the boundary precisely: bytes already delivered cannot be recalled. test revoke between check/commit and check/enqueue, pending room flushes with multiple authors, edit-to-read downgrade, lost replies/idempotent retry, and owner deletion. do not discard another author's accepted pending edits when one recipient loses access. **P1 · filtering journal replay by current grants can suppress the very purge event a revoked device needs (L91, L113; Task 3).** after the final grant disappears, a uniform “current access” filter rejects `sharing.revoked` too. define recipient-addressed control tombstones that remain replayable without content rights, with sufficient stable identity to purge/rescue but no note body. establish monotonic grant generations/source versions so delayed `granted`/`changed` events cannot resurrect a revoked copy after revoke/regrant. define snapshot/reset recovery for a cursor older than journal retention and atomic application with the client cursor. gates must include two offline devices, crash during rescue/purge and replay after retention, not only one live recipient seeing one event. this is required to fulfill Q16, ADRs 0044 and 0047. **P1 · app-password scope intent is sound; the end-to-end enforcement contract is incomplete (L75; Task 1).** the plan explicitly refuses login, setup/recovery, passkeys, account changes and unrelated DAV protocols, and reserves grants for `notes.share`. however, it accepts credentials “only on notes routes” while mutations actually use the generic `/api/v1/actions/{name}`. at this head, `Authenticator::Identity` returns only user/Installation and `api::Signed` creates a `Caller` with no credential kind/scopes; `run_action` dispatches arbitrary registered names, and journal reads receive only the user. Task 1 must name the identity/Caller/executor changes that preserve and enforce the scope ceiling on **every dispatched action and sync operation**, not merely a route check. specify whether scopes imply one another, how `sharing.list`/`shared-with-me`/search are classified, and how mixed sync or bulk payloads are checked per operation. an owner user id must never bypass the Installation's scope ceiling. a `notes.write` password must fail grant/revoke even when its user owns the note; `notes.share` still cannot reshare somebody else's note. require a deny-by-default capability map and tests through the generic endpoint, cached idempotent outcomes, credential revocation and an established websocket. there is no demonstrated bypass here yet; this missing contract prevents approval. **P2 · fan-out is O(R) events plus recipient indexing work, with unbounded global write pressure (L64, L100–104).** batching delivery at 100 rows does not bound the preceding transaction that enumerates all recipients, queued bytes, oldest-event latency or work across all owners. shared FTS duplicates O(R × B) text overall; updating it can dominate compact journal events. define a slice-1 recipient limit, bounded enumeration/queueing, fairness, backlog limits, metrics and restart behavior. keep owner edits independent of recipient indexing. spell out grant-time snapshot plus journal watermark and deduplication so a share created while the worker is behind neither misses an edit nor replays arbitrary earlier events. define per-recipient order and what happens if the source journal is compacted before the fan-out cursor catches up. measure owner edit latency and recipient catch-up at the supported recipient limit. **P2 · search correctness needs bounded candidate refill and full-pipeline isolation (L94, L105; Task 4).** K returned matches is not the amount of authorization work when many stale candidates are denied. specify capped candidate scanning/refill, ordering and pagination so revoked rows neither hide readable results nor dominate latency. grant checks after matching do not alone address per-index vocabulary/typo suggestions, ranking statistics, previews or cached recents derived from revoked text. extend the stale-index gate to these outputs, and retain ADR 0031's measured first-results budget. ### concrete cuts - ship **named-person, single-note, view/edit** with the existing visibility rule. keep Views, links/QR, group audiences and custom names deferred as proposed. use a concrete fixture/deployment prerequisite for Claude/kayg visibility. - keep one active named grant per recipient and effective-permission resolution, but defer runnable synthetic link/group overlap machinery and its UI until those audiences exist. retain a schema extension path. - separate the app-password Settings UI from the sharing implementation PR if needed; creation/list/revoke already have APIs after #782. keep scoped auth enforcement and one-time secret behavior as prerequisites. use the existing generic action adapter, not a parallel notes protocol. - avoid an additional standalone FTS subsystem if the existing SearchProvider/index pipeline can index shared references. keep shared search, history authorship, attachment inheritance and palette share management, which are part of the decided useful flow. - cap recipients for this slice and use the existing journal for delivery; do not add future audience features to justify an unrestricted fan-out framework. **do not cut** authorization coverage, durable revocation tombstones, offline rescue/purge or race gates. **approval requires:** reconcile the complete decided record without another grill; make the authorization/scope boundary and revoke ordering concrete; specify durable multi-device rescue/purge; and bound fan-out. no P0 finding is established by this plan-only review.
Author
Owner

Revised the plan in place following comment #72305:

  • Recorded Architecture round 1 (A1–A13, G1–G7 and settled Q10/Q11/Q14/Q16, SH5 and ADR 0060); the open grill now contains only the notes.share scope and View-history questions.
  • Defined the ordered grant, note-commit and journal-delivery sequence, including the revocation point and suppression of buffered content.
  • Made purge and rescue controls durable per Installation, including cursor reset, two offline devices, authored-block rescue and cache/attachment cleanup.
  • Added the end-to-end app-password capability contract for the generic action executor and every sync operation.
  • Added direct-resource checks, bounded search/fan-out behavior, migration recovery and the access, revocation, search, scope and scale gates.
  • Clarified recipient-only hiding, palette share management and the mockups required before UI implementation.

Documentation-only update. rtk git diff --check passed; no implementation tests were run.

Revised the plan in place following comment #72305: - Recorded Architecture round 1 (A1–A13, G1–G7 and settled Q10/Q11/Q14/Q16, SH5 and ADR 0060); the open grill now contains only the `notes.share` scope and View-history questions. - Defined the ordered grant, note-commit and journal-delivery sequence, including the revocation point and suppression of buffered content. - Made purge and rescue controls durable per Installation, including cursor reset, two offline devices, authored-block rescue and cache/attachment cleanup. - Added the end-to-end app-password capability contract for the generic action executor and every sync operation. - Added direct-resource checks, bounded search/fan-out behavior, migration recovery and the access, revocation, search, scope and scale gates. - Clarified recipient-only hiding, palette share management and the mockups required before UI implementation. Documentation-only update. `rtk git diff --check` passed; no implementation tests were run.
Author
Owner

Independent plan review, round 2

verdict: revise. reviewed docs/plans/0409-sharing-slice-1.md at c6f2903405, round 1 #72305, the planner's reply #72336, and the recorded sharing decisions. this is a plan review; findings concern missing or conflicting contracts, not demonstrated implementation failures. no P0 established.

round-1 status

item status at this head
Q10: tags/cover versus personal pin/place resolved, including scopes and gates
Q14: owner revoke/delete/restore versus recipient hide resolved
Q11/shared search, SH5 palette, ADR 0060 visibility resolved
complete A/G/Q decision record and decision-to-gate map resolved; later audiences/lifecycle remain deferred
Q16: own-block/title rescue, per-device purge, no offline link editing recorded correctly; downgrade edge remains below
P1 mandatory authorization boundary and omitted direct paths resolved as a plan contract: executor, authorized resource handle, classification/refusal gate
P1 ordering across durable writes and buffered delivery partially resolved: common sequence exists; acceptance and actual read/delivery boundaries remain ambiguous
P1 durable revoke controls beyond access filtering/retention resolved for full loss of Read: per-Installation controls, local rescue before purge, upload before ack, replay/reset
P1 scope ceiling through generic actions and sync resolved as an end-to-end contract: Identity/Caller/executor, independent scopes, deny-by-default, per-operation sync, idempotency and websocket checks
P2 fan-out bounds and search refill mostly resolved: 32 recipients, bounded queues/batches, fairness/reset, authorized search outputs and continuation; cost/lock qualification remains below

notes.share issuance is still explicitly open in both source and plan. resolve that existing choice before implementation; its recommendation is not an approved decision. View history remains a later-slice question.

findings

P1 · journal publication order does not yet define a race-free read/delivery boundary (L84–90, L109–123; gate 2).

the shared sequence protects source commits and recipient-journal appends. it does not say that journal reads, snapshot construction, resource-handle use or socket output participate in an atomic authorization/delivery decision. concrete counter-interleaving: a reader checks generation g and prepares a content response; revoke commits g+1; the reader enqueues or streams the prepared response. an AuthorizedResource obtained before revoke and an “immediately before” check do not close this gap. a pre-revoke journal append can also be read after revoke; append order alone is insufficient. the table requires current checks, but does not specify their atomic boundary with revoke.

define the observable cutover precisely and use it consistently. readers should take a versioned snapshot, authorize it and discard/retry stale work at the defined admission point, without holding the writer sequencer across read I/O, response construction, queue backpressure or network sends. explicitly distinguish server admission from bytes already admitted/in flight: the latter can arrive after revoke and cannot be recalled. if the plan retains the stronger literal “no note bytes delivered after the revocation point” promise, explain how it can hold without writers waiting for readers; the current design supplies neither proof nor mechanism. add barriers at read-check/response admission and admission/socket-send, not only publisher-check/journal-enqueue.

P1 · the sequenced acceptance point is not mapped to the existing room's durable history and live publication (L84, L122; Task 3).

the current room saves files after a quiet interval, while edits can enter durable history and live room state earlier (crates/notes/src/room/save.rs, edit.rs). the plan groups note/history/source-journal commit into one slot and separately says to check operations and flushes, but does not identify which existing step is acceptance. a later flush check cannot undo an earlier history commit or live update.

walk the required interleaving explicitly: A has an edit in flight; revoke of A's Edit commits at s; A's write reaches the server; B reads its journal. if A has not durably accepted before s, its operation must fail before modifying authoritative history or shared room state, produce no source/fan-out event, and remain locally available for rescue. B receives no event or room update containing that operation. if A was durably accepted before s, classify it as an earlier accepted operation even when file materialization occurs later. B, if independently authorized, can receive that earlier operation; A's later access still follows the cutover.

name the room/history acceptance step that acquires the sequence and carries actor, credential authority and generation. later file flush should materialize accepted operations, rather than reauthorize a mixed-author document under one caller. specify how a valid B operation continues when an unaccepted A operation is rejected, and how precommit room broadcasts are prevented. add a gate that pauses between room receipt, durable history acceptance, live broadcast and file flush. the existing multi-author gate names the race but does not settle this essential design choice.

P2 · Edit-to-Read has no explicit rescue trigger (L88–90, L104, L148).

controls and rescue are specified for revoke/delete, and L88 explicitly says no purge while Read survives. after an Edit-to-Read downgrade, A can retain unsent edits that now fail permanently without receiving the full-revocation rescue/purge control. define a loss-of-Edit transition that rescues rejected/pending authored edits, retires their shared-write outbox entries, and retains the authorized readable copy. test it separately from final loss of Read; do not force a body purge just to rescue edits.

P2 · O(1) owner work needs an explicit outbox and lock qualification (L84–86, L130–138).

with R recipients, an edit needs O(R) journal appends and grant/generation checks, plus up to O(R × B) shared indexing work. each publication also takes the same resource sequence: moving it to a worker does not remove its contention with writers. source commit is O(1) in R only if it records one source intent/watermark, rather than enumerating recipients or persisting R per-recipient rows inside that slot. clarify which representation is intended. define one short append/validation slot per recipient, release it before indexing, and never wait under it for a recipient queue, reader or socket. a stalled recipient must not hold the resource sequence. gate 6 should assert writer progress while a reader is paused and a recipient queue is full, alongside the latency measurement. the search formula also needs C grant checks, not just K: O(C log G) with individual indexed checks, or state the batched lookup assumption.

smallest useful slice and concrete cuts

the core slice remains useful: named single-note Read/Edit, scope enforcement, live sync, shared search, history authorship, attachments, owner revoke, recipient hide and durable rescue/purge. the extra access and recovery work follows those decisions. the revision nevertheless adds avoidable breadth:

  • move the full app-password Settings UI to its own prerequisite/follow-up PR; use the existing create/list/revoke APIs for the initial agent flow. keep one-time secret handling and capability enforcement mandatory.
  • defer folder-ID backfill from this implementation migration. retain A8 as settled and assign stable folder IDs in the prerequisite that introduces their use; Slice 1 grants reference existing note IDs only.
  • remove shared-View warning fixtures from Task 0's Slice 1 approval list; keep the settled warning with Slice 2.
  • keep unsupported export/archive/notification paths as explicit shared-resource refusals. do not build new shared versions of those features merely to fill the inventory.
  • reuse the existing room actor/history durability boundary for sequencing where possible. do not turn the fallback “prepared/committed recovery phase” into a general transaction framework; name the minimal persisted state and crash-recovery owner for this note-only case.

do not cut controls, attribution, rescue, scope checks, shared search or concurrency gates. approval requires resolving the two P1 boundaries and making reader-independent writer progress explicit. review only: no builds, tests, commits or pushes performed.

## Independent plan review, round 2 **verdict: revise.** reviewed `docs/plans/0409-sharing-slice-1.md` at **c6f2903405407295e4b8b06ec9424bed64f2df89**, round 1 [#72305](https://git.kayg.org/kayg/calternal/pulls/823#issuecomment-72305), the planner's reply [#72336](https://git.kayg.org/kayg/calternal/pulls/823#issuecomment-72336), and the recorded sharing decisions. this is a plan review; findings concern missing or conflicting contracts, not demonstrated implementation failures. no P0 established. ### round-1 status | item | status at this head | |---|---| | Q10: tags/cover versus personal pin/place | resolved, including scopes and gates | | Q14: owner revoke/delete/restore versus recipient hide | resolved | | Q11/shared search, SH5 palette, ADR 0060 visibility | resolved | | complete A/G/Q decision record and decision-to-gate map | resolved; later audiences/lifecycle remain deferred | | Q16: own-block/title rescue, per-device purge, no offline link editing | recorded correctly; downgrade edge remains below | | P1 mandatory authorization boundary and omitted direct paths | resolved as a plan contract: executor, authorized resource handle, classification/refusal gate | | P1 ordering across durable writes and buffered delivery | partially resolved: common sequence exists; acceptance and actual read/delivery boundaries remain ambiguous | | P1 durable revoke controls beyond access filtering/retention | resolved for full loss of Read: per-Installation controls, local rescue before purge, upload before ack, replay/reset | | P1 scope ceiling through generic actions and sync | resolved as an end-to-end contract: Identity/Caller/executor, independent scopes, deny-by-default, per-operation sync, idempotency and websocket checks | | P2 fan-out bounds and search refill | mostly resolved: 32 recipients, bounded queues/batches, fairness/reset, authorized search outputs and continuation; cost/lock qualification remains below | `notes.share` issuance is still explicitly open in both source and plan. resolve that existing choice before implementation; its recommendation is not an approved decision. View history remains a later-slice question. ### findings **P1 · journal publication order does not yet define a race-free read/delivery boundary (L84–90, L109–123; gate 2).** the shared sequence protects source commits and recipient-journal appends. it does not say that journal reads, snapshot construction, resource-handle use or socket output participate in an atomic authorization/delivery decision. concrete counter-interleaving: a reader checks generation g and prepares a content response; revoke commits g+1; the reader enqueues or streams the prepared response. an `AuthorizedResource` obtained before revoke and an “immediately before” check do not close this gap. a pre-revoke journal append can also be read after revoke; append order alone is insufficient. the table requires current checks, but does not specify their atomic boundary with revoke. define the observable cutover precisely and use it consistently. readers should take a versioned snapshot, authorize it and discard/retry stale work at the defined admission point, without holding the writer sequencer across read I/O, response construction, queue backpressure or network sends. explicitly distinguish server admission from bytes already admitted/in flight: the latter can arrive after revoke and cannot be recalled. if the plan retains the stronger literal “no note bytes delivered after the revocation point” promise, explain how it can hold without writers waiting for readers; the current design supplies neither proof nor mechanism. add barriers at read-check/response admission and admission/socket-send, not only publisher-check/journal-enqueue. **P1 · the sequenced acceptance point is not mapped to the existing room's durable history and live publication (L84, L122; Task 3).** the current room saves files after a quiet interval, while edits can enter durable history and live room state earlier (`crates/notes/src/room/save.rs`, `edit.rs`). the plan groups note/history/source-journal commit into one slot and separately says to check operations and flushes, but does not identify which existing step is acceptance. a later flush check cannot undo an earlier history commit or live update. walk the required interleaving explicitly: A has an edit in flight; revoke of A's Edit commits at s; A's write reaches the server; B reads its journal. if A has not durably accepted before s, its operation must fail before modifying authoritative history or shared room state, produce no source/fan-out event, and remain locally available for rescue. B receives no event or room update containing that operation. if A was durably accepted before s, classify it as an earlier accepted operation even when file materialization occurs later. B, if independently authorized, can receive that earlier operation; A's later access still follows the cutover. name the room/history acceptance step that acquires the sequence and carries actor, credential authority and generation. later file flush should materialize accepted operations, rather than reauthorize a mixed-author document under one caller. specify how a valid B operation continues when an unaccepted A operation is rejected, and how precommit room broadcasts are prevented. add a gate that pauses between room receipt, durable history acceptance, live broadcast and file flush. the existing multi-author gate names the race but does not settle this essential design choice. **P2 · Edit-to-Read has no explicit rescue trigger (L88–90, L104, L148).** controls and rescue are specified for revoke/delete, and L88 explicitly says no purge while Read survives. after an Edit-to-Read downgrade, A can retain unsent edits that now fail permanently without receiving the full-revocation rescue/purge control. define a loss-of-Edit transition that rescues rejected/pending authored edits, retires their shared-write outbox entries, and retains the authorized readable copy. test it separately from final loss of Read; do not force a body purge just to rescue edits. **P2 · O(1) owner work needs an explicit outbox and lock qualification (L84–86, L130–138).** with R recipients, an edit needs O(R) journal appends and grant/generation checks, plus up to O(R × B) shared indexing work. each publication also takes the same resource sequence: moving it to a worker does not remove its contention with writers. source commit is O(1) in R only if it records one source intent/watermark, rather than enumerating recipients or persisting R per-recipient rows inside that slot. clarify which representation is intended. define one short append/validation slot per recipient, release it before indexing, and never wait under it for a recipient queue, reader or socket. a stalled recipient must not hold the resource sequence. gate 6 should assert writer progress while a reader is paused and a recipient queue is full, alongside the latency measurement. the search formula also needs C grant checks, not just K: O(C log G) with individual indexed checks, or state the batched lookup assumption. ### smallest useful slice and concrete cuts the core slice remains useful: named single-note Read/Edit, scope enforcement, live sync, shared search, history authorship, attachments, owner revoke, recipient hide and durable rescue/purge. the extra access and recovery work follows those decisions. the revision nevertheless adds avoidable breadth: - move the full app-password Settings UI to its own prerequisite/follow-up PR; use the existing create/list/revoke APIs for the initial agent flow. keep one-time secret handling and capability enforcement mandatory. - defer folder-ID backfill from this implementation migration. retain A8 as settled and assign stable folder IDs in the prerequisite that introduces their use; Slice 1 grants reference existing note IDs only. - remove shared-View warning fixtures from Task 0's Slice 1 approval list; keep the settled warning with Slice 2. - keep unsupported export/archive/notification paths as explicit shared-resource refusals. do not build new shared versions of those features merely to fill the inventory. - reuse the existing room actor/history durability boundary for sequencing where possible. do not turn the fallback “prepared/committed recovery phase” into a general transaction framework; name the minimal persisted state and crash-recovery owner for this note-only case. do not cut controls, attribution, rescue, scope checks, shared search or concurrency gates. **approval requires resolving the two P1 boundaries and making reader-independent writer progress explicit.** review only: no builds, tests, commits or pushes performed.
Author
Owner

Updated the plan at d4b64dc5:

  • Replaced the multi-part ordering scheme with one per-note admission sequence; revocation uses the same writer lock, while readers filter committed snapshots without blocking writers.
  • Kept ordered fan-out outside the lock; durable controls bypass grant filtering, with per-device rescue/purge and Edit-to-Read behavior specified.
  • Added the A-edit/revoke/late-arrival/B-journal and randomized interleaving gates, writer-progress coverage, and action-pipeline/sync scope tests.
  • Deferred app-password Settings UI, folder-ID backfill and View-warning mockups; corrected search complexity.

Documentation only; rtk git diff --check passed.

Updated the plan at d4b64dc5: - Replaced the multi-part ordering scheme with one per-note admission sequence; revocation uses the same writer lock, while readers filter committed snapshots without blocking writers. - Kept ordered fan-out outside the lock; durable controls bypass grant filtering, with per-device rescue/purge and Edit-to-Read behavior specified. - Added the A-edit/revoke/late-arrival/B-journal and randomized interleaving gates, writer-progress coverage, and action-pipeline/sync scope tests. - Deferred app-password Settings UI, folder-ID backfill and View-warning mockups; corrected search complexity. Documentation only; `rtk git diff --check` passed.
Author
Owner

Independent plan review, round 3 (final)

verdict: revise. reviewed docs/plans/0409-sharing-slice-1.md at d4b64dc5510589f9a248d1afd0b8d0e4791aa4d3, the settled docs/grills/sharing.md, rounds 1 and 2, and the planner's change list. plan review only, not evidence of an implemented failure. no P0 established. this is the final review round; the remaining correction is described below without requesting another round or another grill.

check status and interleaving
round-2 P1: late edits reaching history/room resolved as a plan contract, L81–83. A stages an edit; revoke commits at R; A arrives later: admission refuses it before sequence/history/room/journal effects. independently authorized B can continue. if A instead commits at s<R, its history and later file flush remain valid.
room broadcast source acceptance resolved: only admitted operations can broadcast. a pre-R admitted update may still arrive after R; application after a purge needs the fence below. an unadmitted late update must never broadcast.
round-2 P1: journal read/delivery cutover partially resolved. committed journal-and-grant snapshots give a precise historical eligibility rule without blocking writers. they intentionally permit an old s<R entry after revoke; they do not by themselves establish safe application after the revoke control.
Edit→Read resolved in L85, L99 and gate 3: subsequent writes fail, local pending edits are rescued/retired, readable content stays. Task 5 has a conflicting shorthand below.
revoke/re-grant, several intervals retained grant rows can represent [g1,R1), [g2,R2), etc. content in each gap is excluded; a new grant identifies its initial committed version. an old interval remains eligible forever, so interval union alone does not fence replay against the new baseline or an already-applied control.
fan-out lagging revoke source intent is durable and workers preserve per-note order outside the lock. source order alone does not protect a client that consumes controls before older content, as L99 requires, or receives an older room update from another channel.
writer progress resolved as a plan contract, L71/L83/L85/L130. one source journal entry and one intent, no recipient enumeration, fan-out, indexing, network/queue waits or reader waits under the writer lock. keep authorization reads inside the admission transaction and file/history materialization outside it. gate 6 explicitly pauses a reader and fills a recipient queue while writers progress.
settled decisions no new policy contradiction found with A1–A13, G1–G7, Q10/Q11/Q14/Q16 and SH1–SH10; later audiences remain deferred. the replay gap below prevents attesting to Q16c. notes.share issuance remains genuinely open in both documents; the plan correctly requires resolving it before enabling that scope.

findings

P1 · a purge control is not yet a durable barrier to older, interval-eligible content (L81–85, L98–99, L143; gates 2–3). This is the remaining part of round 2's read/delivery finding, not a request to hold readers or sockets under the writer lock.

Concrete trace: grant starts at 2; an authorized edit commits at 8; revoke commits at 9. A reconnecting device consumes control 9 first, rescues/purges and acknowledges it, as specified. A delayed entry/update 8 still passes 2 ≤ 8 < 9. Nothing in the application contract says it cannot restore room/cache state or apply content after that purge. Journal events are references, so a fresh byte fetch is correctly denied by current-access checks; that protects the fetch, but does not settle delayed room payloads or already prepared content reads. This concerns state after the client has applied the control, not recalling bytes already in flight.

Re-grant makes the omission observable without a denied fetch: grant at 12 identifies a new committed baseline, then a delayed entry from the old [2,9) interval is still interval-eligible. The plan does not define how that old notification/update is prevented from rolling state back, or how a delayed old revoke control avoids purging the new baseline. Per-note fan-out ordering is useful but insufficient when controls are processed ahead of content or room/read responses travel independently.

Small correction: persist a per-note client control/baseline watermark with the purge/rescue state. Historical interval eligibility may still permit journal references, but applying content, room updates and completed reads must respect that watermark and the currently installed authorized baseline. Old entries cannot recreate a purged copy; a newer re-grant installs its identified authorized baseline before accepting its updates; older controls cannot remove that newer baseline. Define this once for sync, room and read completion, retaining lock-free readers and the existing source-admission invariant. This needs no general transaction framework or new audience machinery.

Required deterministic gates: (1) pause a revoked recipient's own read/update after preparation, apply and acknowledge its revoke control, then release the old response/update and assert the shared copy stays purged; (2) drain lagging pre-R fan-out after control processing and assert the same; (3) perform grant→revoke→grant→revoke→grant with edits in each interval and gap, install the latest baseline, then replay old content and controls across channels/restart and assert no rollback, gap exposure or new-copy purge. Include an admitted pre-R room broadcast and an unadmitted post-R edit. Gate 2 currently reads as independently authorized B and asserts interval membership, which these old entries satisfy; gate 3 asserts purge but not its persistence after delayed delivery. Randomization does not replace these barriers.

P2 · Task 5 says to purge on every control, contradicting Edit→Read (L242 versus L85/L99 and gate 3). Change that checklist item to rescue/retire on loss of Edit and purge only on loss of Read, including owner deletion. Otherwise a literal UI implementation removes a still-readable copy. No new policy decision is needed.

No other new P0–P3 findings.

suggested PR split

Three dependent implementation PRs are realistic; two would combine too much cross-layer work. Keep this plan PR separate.

  1. scoped identity and admission foundation: after #782, Notes scopes through Identity/Caller/executor/sync, deny-by-default classification, grant schema/resolver and typed actions, and source journal/room/history admission under the note writer lock. Add focused admission, migration, scope and access tests. Keep sharing unavailable until the next PR supplies complete read/control enforcement.
  2. complete shared data and recovery: authorized resource handling across reads/bytes/rooms/sync, ordered bounded fan-out, durable controls and per-device rescue/purge/application fence, shared search/history/reference projection, lifecycle/reset/restart behavior. Run the complete isolation, interval/race and writer-progress gates before enabling the scripted owner→recipient flow. Reuse existing journal, search and room code; unsupported export/notification routes refuse shared resources.
  3. approved UI and end-to-end completion: fixture approval first, then Share/palette, share sheet, protected “Shared with me,” hide, attribution and recovery states; focused browser/accessibility checks plus the scripted agent flow and final latency evidence. app-password Settings UI, folder backfill and future audiences remain separate follow-ups.

Each PR needs its own relevant merge gates; “PR 1 gates”/“all gates for the implementation PR” should become final slice acceptance gates plus the per-PR subsets. Do not defer backend isolation or recovery correctness to the UI PR.

final verdict: revise for the replay/application fence and the downgrade checklist correction. The source-admission simplification is sound and the writer section is bounded. review only: no builds, tests, repository edits, commits or pushes performed.

## Independent plan review, round 3 (final) **verdict: revise.** reviewed `docs/plans/0409-sharing-slice-1.md` at `d4b64dc5510589f9a248d1afd0b8d0e4791aa4d3`, the settled `docs/grills/sharing.md`, rounds [1](https://git.kayg.org/kayg/calternal/pulls/823#issuecomment-72305) and [2](https://git.kayg.org/kayg/calternal/pulls/823#issuecomment-72343), and the planner's [change list](https://git.kayg.org/kayg/calternal/pulls/823#issuecomment-72367). plan review only, not evidence of an implemented failure. no P0 established. this is the final review round; the remaining correction is described below without requesting another round or another grill. | check | status and interleaving | |---|---| | round-2 P1: late edits reaching history/room | resolved as a plan contract, L81–83. A stages an edit; revoke commits at R; A arrives later: admission refuses it before sequence/history/room/journal effects. independently authorized B can continue. if A instead commits at s<R, its history and later file flush remain valid. | | room broadcast | source acceptance resolved: only admitted operations can broadcast. a pre-R admitted update may still arrive after R; application after a purge needs the fence below. an unadmitted late update must never broadcast. | | round-2 P1: journal read/delivery cutover | partially resolved. committed journal-and-grant snapshots give a precise historical eligibility rule without blocking writers. they intentionally permit an old s<R entry after revoke; they do not by themselves establish safe application after the revoke control. | | Edit→Read | resolved in L85, L99 and gate 3: subsequent writes fail, local pending edits are rescued/retired, readable content stays. Task 5 has a conflicting shorthand below. | | revoke/re-grant, several intervals | retained grant rows can represent [g1,R1), [g2,R2), etc. content in each gap is excluded; a new grant identifies its initial committed version. an old interval remains eligible forever, so interval union alone does not fence replay against the new baseline or an already-applied control. | | fan-out lagging revoke | source intent is durable and workers preserve per-note order outside the lock. source order alone does not protect a client that consumes controls before older content, as L99 requires, or receives an older room update from another channel. | | writer progress | resolved as a plan contract, L71/L83/L85/L130. one source journal entry and one intent, no recipient enumeration, fan-out, indexing, network/queue waits or reader waits under the writer lock. keep authorization reads inside the admission transaction and file/history materialization outside it. gate 6 explicitly pauses a reader and fills a recipient queue while writers progress. | | settled decisions | no new policy contradiction found with A1–A13, G1–G7, Q10/Q11/Q14/Q16 and SH1–SH10; later audiences remain deferred. the replay gap below prevents attesting to Q16c. `notes.share` issuance remains genuinely open in both documents; the plan correctly requires resolving it before enabling that scope. | ### findings **P1 · a purge control is not yet a durable barrier to older, interval-eligible content (L81–85, L98–99, L143; gates 2–3).** This is the remaining part of round 2's read/delivery finding, not a request to hold readers or sockets under the writer lock. Concrete trace: grant starts at 2; an authorized edit commits at 8; revoke commits at 9. A reconnecting device consumes control 9 first, rescues/purges and acknowledges it, as specified. A delayed entry/update 8 still passes `2 ≤ 8 < 9`. Nothing in the application contract says it cannot restore room/cache state or apply content after that purge. Journal events are references, so a fresh byte fetch is correctly denied by current-access checks; that protects the fetch, but does not settle delayed room payloads or already prepared content reads. This concerns state after the client has applied the control, not recalling bytes already in flight. Re-grant makes the omission observable without a denied fetch: grant at 12 identifies a new committed baseline, then a delayed entry from the old [2,9) interval is still interval-eligible. The plan does not define how that old notification/update is prevented from rolling state back, or how a delayed old revoke control avoids purging the new baseline. Per-note fan-out ordering is useful but insufficient when controls are processed ahead of content or room/read responses travel independently. **Small correction:** persist a per-note client control/baseline watermark with the purge/rescue state. Historical interval eligibility may still permit journal references, but applying content, room updates and completed reads must respect that watermark and the currently installed authorized baseline. Old entries cannot recreate a purged copy; a newer re-grant installs its identified authorized baseline before accepting its updates; older controls cannot remove that newer baseline. Define this once for sync, room and read completion, retaining lock-free readers and the existing source-admission invariant. This needs no general transaction framework or new audience machinery. **Required deterministic gates:** (1) pause a revoked recipient's own read/update after preparation, apply and acknowledge its revoke control, then release the old response/update and assert the shared copy stays purged; (2) drain lagging pre-R fan-out after control processing and assert the same; (3) perform grant→revoke→grant→revoke→grant with edits in each interval and gap, install the latest baseline, then replay old content and controls across channels/restart and assert no rollback, gap exposure or new-copy purge. Include an admitted pre-R room broadcast and an unadmitted post-R edit. Gate 2 currently reads as independently authorized B and asserts interval membership, which these old entries satisfy; gate 3 asserts purge but not its persistence after delayed delivery. Randomization does not replace these barriers. **P2 · Task 5 says to purge on every control, contradicting Edit→Read (L242 versus L85/L99 and gate 3).** Change that checklist item to rescue/retire on loss of Edit and purge only on loss of Read, including owner deletion. Otherwise a literal UI implementation removes a still-readable copy. No new policy decision is needed. No other new P0–P3 findings. ### suggested PR split Three dependent implementation PRs are realistic; two would combine too much cross-layer work. Keep this plan PR separate. 1. **scoped identity and admission foundation:** after #782, Notes scopes through Identity/Caller/executor/sync, deny-by-default classification, grant schema/resolver and typed actions, and source journal/room/history admission under the note writer lock. Add focused admission, migration, scope and access tests. Keep sharing unavailable until the next PR supplies complete read/control enforcement. 2. **complete shared data and recovery:** authorized resource handling across reads/bytes/rooms/sync, ordered bounded fan-out, durable controls and per-device rescue/purge/application fence, shared search/history/reference projection, lifecycle/reset/restart behavior. Run the complete isolation, interval/race and writer-progress gates before enabling the scripted owner→recipient flow. Reuse existing journal, search and room code; unsupported export/notification routes refuse shared resources. 3. **approved UI and end-to-end completion:** fixture approval first, then Share/palette, share sheet, protected “Shared with me,” hide, attribution and recovery states; focused browser/accessibility checks plus the scripted agent flow and final latency evidence. app-password Settings UI, folder backfill and future audiences remain separate follow-ups. Each PR needs its own relevant merge gates; “PR 1 gates”/“all gates for the implementation PR” should become final slice acceptance gates plus the per-PR subsets. Do not defer backend isolation or recovery correctness to the UI PR. **final verdict: revise for the replay/application fence and the downgrade checklist correction.** The source-admission simplification is sound and the writer section is bounded. review only: no builds, tests, repository edits, commits or pushes performed.
Author
Owner

Updated the plan at 9bf4a9c5:

  • Added the durable per-note device fence and server-side serving rule; purge advances it to R, and re-grant installs a full snapshot at G.
  • Added delayed-after-purge and delayed-after-re-grant interleavings, including prepared reads, room updates, fan-out, direct requests and randomized schedules.
  • Corrected Edit-to-Read so readable content stays and only edits refused after downgrade are rescued.
  • Split implementation into three PRs: scoped auth/admission, shared data/recovery/search, and approved UI/end-to-end checks; mapped gates per PR.

Documentation only; rtk git diff --check passed.

Updated the plan at 9bf4a9c5: - Added the durable per-note device fence and server-side serving rule; purge advances it to R, and re-grant installs a full snapshot at G. - Added delayed-after-purge and delayed-after-re-grant interleavings, including prepared reads, room updates, fan-out, direct requests and randomized schedules. - Corrected Edit-to-Read so readable content stays and only edits refused after downgrade are rescued. - Split implementation into three PRs: scoped auth/admission, shared data/recovery/search, and approved UI/end-to-end checks; mapped gates per PR. Documentation only; `rtk git diff --check` passed.
Author
Owner

Closing per #931: the sharing slice made this plan out of date (#827/#829).

Closing per #931: the sharing slice made this plan out of date (#827/#829).
kayg closed this pull request 2026-11-04 17:37:44 +00:00
Some checks failed
CI / server (pull_request) Has been cancelled
CI / rust (pull_request) Has been cancelled
CI / web (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal!823
No description provided.