Sloth-ninja turns repeatable reviews into firm assets

This fork gives firms a shared home for the tabular review structures teams keep rebuilding matter by matter.

workflowsecurity

The new Templates area lets a team build a review layout once, save it, and choose it again when starting similar work. That matters for recurring diligence, investigations, and any process where consistency is part of the product.

  • Shared templates let approved firm users reuse the same columns and review structure across matters.
  • An editor and template picker support creating, selecting, and saving review layouts without rebuilding them from scratch.
  • Administrative controls cover changes and reversions, with multi-factor authentication required for sensitive reversions.
  • Email sharing and existing project screens have been accounted for, alongside safeguards when a supporting data service is unavailable.

This is a substantial, tested feature train rather than a small patch, so firms considering adoption should review the full GitHub work before importing it.

So what Practice-support and legal-ops teams running repeatable review workflows should care because this makes standardisation usable at the point of work.

View this fork on GitHub →

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

Commits in this thread

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

SHA Subject Author Date
02522115 Review templates backend: tabularTemplates seam + /tabular-templates routes sloth-ninja 2026-08-05 ↗ GitHub
commit body
Plan Tasks 2-3 (docs/superpowers/plans/2026-08-04-review-templates.md):
self-contained lib/tabularTemplates.ts seam over the existing workflows
table (type='tabular'), thin requireAuth+asyncHandler routes mounted at
/tabular-templates, WS9-style firm sharing (org stamping, owner guard in
the UPDATE predicate, best-effort firm_shared/firm_reverted audit rows),
42703-degrade for the owner-blocked migration, tombstone exclusion at the
seam choke points. 66 new tests (44 seam + 22 route); suite 713 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
78b04a46 Review fixes for #74: PGRST204 degrade, email-shared bucket, route hardening sloth-ninja 2026-08-05 ↗ GitHub
commit body
Review should-fixes: isMissingColumnOrTable now accepts PostgREST's
PGRST204 (UPDATE payloads naming a missing column bypass Postgres and are
rejected from the schema cache - the live pre-migration path; DURABLE_
LESSONS 2026-08-05 appended); TemplateList gains the spec'd `shared`
bucket (workflow_shares email match, own/tombstoned/is_system excluded)
and listTemplates takes the caller's email. Nits: uuid front-guard on all
:id routes (uniform 404, no 22P02-as-500, catches the /admin typo),
tombstone predicate on admin revert, >=1 column required, clearer
non-object column message, control characters stripped from column names
and tag values. Suite 725 green (53 seam + 25 route).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
051c20ed Review templates Task 1: migration, schema sync, spec + plan sloth-ninja 2026-08-05 ↗ GitHub
commit body
Owner-authorised 20260804_01_workflow_firm_visibility.sql (additive:
workflows.visibility + organisation_id + partial firm index), schema.sql
kept in sync, owner-approved spec and implementation plan committed,
allowlist entry (owner edit) recorded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
b0a25e3e Merge origin/main (#73) into templates-backend sloth-ninja 2026-08-05 ↗ GitHub
commit body
Docs-only conflicts: BUILD_LOG keeps all entries newest-first (#74 entry
above #73's fix-wave entry); DURABLE_LESSONS keeps BOTH 2026-08-05 entries
(KDF-timeout from #73 first, PGRST204 second) with the Index merged to
match. No code-file conflicts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
99ae9e52 Address review: shared bucket in plan/spec, line ref, check-constraint note sloth-ninja 2026-08-05 ↗ GitHub
commit body
Review findings on #75: plan Task 2 interfaces and Task 4 sections now
match the built backend (shared bucket, userEmail param, getTemplate);
spec Surfaces gains the Shared-with-me section; tabular.ts line ref
corrected; migration documents the no-check-constraint choice (WS9
precedent).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
e9e7c2b3 Merge origin/main (#75 templates migration/spec/plan) into templates-backend sloth-ninja 2026-08-05 ↗ GitHub
BUILD_LOG-only conflict: entries kept newest-first (#74 backend entry above
the #75 migration entry). #75's migration, spec and plan files auto-merged in.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
0ca9b8f9 Delta-review fixes for #74: getTemplate email-share access + hardening nits sloth-ninja 2026-08-05 ↗ GitHub
commit body
Should-fix: getTemplate gains an optional userEmail param and a
workflow_shares normalised-email match as its third access path - the
shared list bucket was unreachable by id, 404ing the editor/duplicate
flow for sharees. Final signature:
getTemplate(db, userId, id, orgId, userEmail?).

Nits: shared-id lookup chunked at 100 per .in() (414 guard) with
newest-first re-sort; a template both email-shared and firm-visible now
appears in shared only (shared wins); tag values additionally strip
[ ] | (the [[tag]] prompt-marker vocabulary; column names keep the
control-char strip only); the PGRST204/42703 degrade in
setTemplateVisibility/adminRevertTemplate now console.warns with
safeErrorLog; test-file raw control bytes replaced with \u escapes.

Suite 738 green (57 seam + 25 route template tests). BUILD_LOG entry
amended in place.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
d8d88f5a Review templates frontend: Templates surface, editor and pickers sloth-ninja 2026-08-05 ↗ GitHub
commit body
Frontend half of the Review templates train (plan Task 4), consuming the
/tabular-templates routes from #74. Builds all five spec surfaces:

- Templates page (/review-templates): My templates / Shared with me / Firm
  templates / Built-in, per-ownership row menus, Firm badge, error+retry gate
- Template editor (/review-templates/[id], plus a client-side draft at /new
  because the server requires at least one column), reusing AddColumnModal
  and columnFormat as the workflow editor does
- Save as template from a review grid's header menu, with a success notice
  linking to the Templates page
- Pickers relabelled "Start from a template" / "Apply template", now listing
  mine + shared + firm + built-in; Workflows page drops tabular rows and
  gains a pointer line
- Admin Firm-templates card with per-item revert (spec surface 5)

Gates: tsc clean; lint identical to the stashed baseline (112 problems, zero
new); next build succeeds with both new routes. No prettier --write.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
8f87aa94 Address review of #76: editor key, keyboard guard, column-save rollback sloth-ninja 2026-08-05 ↗ GitHub
commit body
Should-fixes:
- key={id} on TemplateEditor so state re-initialises per template segment
  (fixes built-in -> Duplicate -> Back stale content and the post-create
  read-only flash)
- stopPropagation on the row actions cell's onKeyDown, so Enter/Space on the
  menu trigger opens the menu instead of navigating the row
- column auto-save now snapshots the previous columns and rolls back on
  failure with an inline error, mirroring the admin card's pattern

Nits: TemplateRow/Section moved to module scope; Duplicate failures surface
the server detail at both sites; the four flagged error sites use the house
`err instanceof MikeApiError && err.message` pattern so network TypeErrors
never render as user copy; save-notice timer cleared on unmount and replaced
on re-save; "Search templates..." ellipsis.

MFA gating for template admin-revert is out of scope here (follow-up PR); the
admin card copy is unchanged.

Gates: tsc clean; lint 112 problems, identical to stashed baseline; build
clean with both new routes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
64114ca6 MFA-gate the template admin-revert, aligning it with routes/admin.ts sloth-ninja 2026-08-05 ↗ GitHub
commit body
The independent review of #76 flagged the template admin-revert as the only
admin mutation in the product not behind requireMfaIfEnrolled, unlike the WS9
firm-library revert the card is modelled on. That was a backend change, so it
was deferred out of #76 into this follow-up.

Backend: POST /tabular-templates/:id/admin-revert gains requireMfaIfEnrolled
between requireAdmin and asyncHandler - the ordering all nine mutating routes
in routes/admin.ts use. Route test asserts an enrolled-but-unverified admin
gets 403 with code "mfa_verification_required" and never reaches the seam.

Frontend: FirmTemplatesSection wraps the whole mutation in the page's existing
useMfaGuardedAction, so it replays intact after a step-up; mfa-required is
rethrown for the guard while other failures still surface inline. Card copy
updated only after the behaviour was true.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
56fb62c5 Merge pull request #75 from Sloth-ninja/templates-migration Ezana.H 2026-08-05 ↗ GitHub
Review templates Task 1: migration, schema sync, spec + plan
ed6f6756 Merge pull request #74 from Sloth-ninja/templates-backend Ezana.H 2026-08-05 ↗ GitHub
Review templates backend: tabularTemplates seam + /tabular-templates routes (plan Tasks 2-3)
e7859510 Merge pull request #76 from Sloth-ninja/templates-frontend Ezana.H 2026-08-05 ↗ GitHub
Review templates frontend: Templates surface, editor and pickers
5da6d5c4 Merge pull request #77 from Sloth-ninja/templates-mfa-alignment Ezana.H 2026-08-05 ↗ GitHub
MFA-gate the template admin-revert (aligns with routes/admin.ts)
6b8f194e Review-templates train: composed-range fix wave sloth-ninja 2026-08-05 ↗ GitHub
commit body
Fixes the cross-commit issues the composed-range review of the whole
templates train (base...5da6d5c) found:

I1+M7 restore the template->review linkage (workflow_id on create and on
in-grid apply, owner-only, uuid-validated server-side) AND org-scope the
usage dashboard's template-title lookup, so a template email-shared from
outside the firm can never print its name on the firm's dashboard.

I2 re-validates a template's stored columns before permitting a flip to
firm visibility (legacy rows written by the older workflow editor), with
a distinct invalid_columns outcome mapped to a 400 telling the owner to
reopen and re-save. Reverting to private is never blocked.

I3 filters hidden built-ins out of both template pickers, so Hide means
the same thing there as on the Templates page. Fails open.

Minor: dead is_system re-filter removed (M1); firm-templates card names
where members share (M2); column modals say "Templates" in the template
editor (M3); pending-deletion rows label templates as Templates (M4);
"Save as template" is disabled with an explanation above 30 columns (M6);
draft work-loss guard on /review-templates/new (M9); 404 on save says the
template is gone (M10); stable-loadOptions note on the picker effect (M11).

No migration. Backend 757 tests (739 baseline, +18).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9652f8df BUILD_LOG: record M5/M8/M12 substance for the templates fix wave sloth-ninja 2026-08-05 ↗ GitHub
commit body
The accepted-with-rationale findings are now written out (unchunked .in()
in enrichOwnerNames; the active-flag idiom vs AbortController time-boxes;
ColumnConfig[] typing vs legacy pass-through format strings), replacing
the by-number placeholder. Docs only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
e2ab61f2 Session close-out: templates train complete, Matters incident resolved sloth-ninja 2026-08-05 ↗ GitHub
commit body
CLAUDE.md Current status (templates train #74-#78 code-complete, v20
deploy, incident resolution, Practice Management next), HANDOVER §0
pickup rewrite with the shipping checklist, DURABLE_LESSONS
migration-verification lesson, BUILD_LOG close-out entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
99453daa Merge pull request #78 from Sloth-ninja/templates-train-fixes Ezana.H 2026-08-06 ↗ GitHub
Review-templates train: composed-range fix wave
1175a1b4 Address review: WS9 record correction, count reword, migration-verify step sloth-ninja 2026-08-06 ↗ GitHub
commit body
Review nits on #79: the stale 'ACTIVE since 03/08' WS9 record now carries
its correction inline (the exact class the new lesson warns about);
Important-findings count reworded to avoid fighting the #78 entry;
HANDOVER step 2 now requires the proving-select output + a columns
diagnostic before ticking off, and states why deploy-before-migration is
safe here; double blank line dropped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
0a2d87a7 Record templates train shipped: v21 + frontend deployed, migration verified, QA passed sloth-ninja 2026-08-06 ↗ GitHub
commit body
Docs-only: CLAUDE.md status to SHIPPED with dates/evidence, BUILD_LOG
shipping entry, DURABLE_LESSONS edge-cache-404 lesson (new routes need a
Cloudflare purge; verify with cache-busters).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
a775b2e0 Merge pull request #79 from Sloth-ninja/templates-closeout Ezana.H 2026-08-06 ↗ GitHub
Session close-out: templates train complete, Matters incident resolved
1f08d9fe Address review: date bump, HANDOVER shipped note, broadened cache lesson, PM decisions mirrored sloth-ninja 2026-08-06 ↗ GitHub
commit body
Review on #80: status stamp 2026-08-06; HANDOVER §0 shipped banner (the
record-vs-reality class the lesson exists for); cache lesson broadened to
every-deploy purge (reviewer measured year-long s-maxage on all pages, no
stale-while-revalidate) with the structural follow-up queued; the 06/08
Practice Management decisions (incl. live-reads/no-DB-copy) mirrored into
Current status; owner-pasted-output attribution made explicit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
680d9115 Retrigger CI (runner starvation on previous runs) sloth-ninja 2026-08-07 ↗ GitHub
d178f027 Merge pull request #80 from Sloth-ninja/templates-shipped Ezana.H 2026-08-07 ↗ GitHub
Record templates train shipped: deploys, migration verified, owner QA

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-1008.md from inside the repo you want the changes in.

⬇ Download capture-thread-1008.md