Sloth-ninja closes the deleted-matter back door

Firm-wide visibility can no longer turn a soft-deleted matter into a live workspace.

securityworkflow

Sloth-ninja has tightened the line between visibility and deletion. Previously, someone at a firm who still held an old link or identifier could reach a matter marked for deletion, including through routes that could generate content or continue a chat. That is now blocked at the shared access layer, while authorised administrators can still restore or permanently remove records during the retention period.

  • Deleted matters are unavailable to owners, shared users and firm viewers alike.
  • Visibility settings cannot be changed on a matter already marked for deletion.
  • Deletion-related audit records now use consistent labels.
  • The firm library is easier to use by keyboard, and member selection better signals when firm-wide options are available.
So what Firms using shared matter libraries should care because deletion now means inaccessible in practice, not merely hidden from the usual screen.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

2 commits from Sloth-ninja/JessicaOSS, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
cee6bc61 WS9 fix wave: compose firm-visibility with deletion governance (WS8×WS9) sloth-ninja 2026-07-28 ↗ GitHub
commit body
Composed-range re-review of the WS9 train surfaced five fixes, all in the
WS8×WS9 interaction class (firm visibility reads through rows deletion
governance tombstones; the two seams were never composed):

1. Parent-tombstone gap (important): tombstoning sets only deleted_at, so a
   tombstoned firm-visible matter/review still passed checkProjectAccess /
   ensureReviewAccess, and the content sub-routes (never re-checking the
   tombstone) let any firm member read - and via /generate, /chat WRITE - a
   soft-deleted item for the whole retention window. Folded the tombstone
   check into both helpers (choke point): tombstoned parent => not-ok for
   everyone incl. the owner. 42703-tolerant. Detail-route guards left as
   defence-in-depth. Restore/expedite/list paths verified unaffected.
2. setResourceVisibility now refuses a tombstoned item (.is deleted_at null
   in the predicate => not_found).
3. Audit resource_type unified to hyphenated "tabular-review" for both firm
   actions (flip route + admin revert param mapping); URL/API keep underscore.
4. PeopleModal add-member placeholder gains firm copy when firm surfaces show.
5. Firm-library rows: role=button + tabIndex + Enter/Space + aria-label.

Tests: +11 (backend 511/511). tsc/eslint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
d21d6d7a Review fix: parent-tombstone gate in ensureDocAccess owner path; tr role=link sloth-ninja 2026-07-28 ↗ GitHub
commit body
Independent review of PR #57 (REQUEST_CHANGES, one should-fix):

- ensureDocAccess returned {ok:true} for the document owner BEFORE reaching
  checkProjectAccess, so the folded-in parent-tombstone gate only fired on the
  non-owner path. A doc owner (or a firm colleague who uploaded a doc into
  someone else's tombstoned firm matter) could still GET/download it for the
  retention window. Added the parent-matter tombstone check before the owner
  short-circuit (mirrors ensureReviewAccess); standalone docs (project_id null)
  unaffected. 42703-tolerant. +2 tests.

- NIT: firm-library <tr> role="button" -> role="link" (navigation semantics;
  keeps tabIndex/handlers/focus style).

Backend 513/513, tsc clean. Frontend tsc/eslint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-1130.md from inside the repo you want the changes in.

⬇ Download capture-thread-1130.md