Sloth-ninja puts firm admins in charge of the AI menu

JessicaOSS now lets a firm decide which AI models its members can use, with the rule enforced where chats are actually sent.

complianceworkflow

The change is less about offering more AI choice than making that choice governable. A firm administrator can set an approved model list for the organisation, giving members a clearer and more controlled set of options when they work in chat.

Crucially, the follow-up work closes an easy-to-miss gap: the policy is checked when a chat request is made, not merely displayed in settings. That means a user cannot bypass the firm's selection simply because the interface looks restricted.

For legal teams balancing experimentation with consistency, this is a sensible control point: choose the AI services you are prepared to use, then make that decision stick.

So what Managing partners, GCs and legal-ops leads should care because firm-level AI controls only matter when they govern real use, not just the settings 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

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

SHA Subject Author Date
e504926a WS8 PR F: firm model preferences sloth-ninja 2026-07-27 ↗ GitHub
commit body
Add a third firm policy (memberModelPrefs) and firm model configuration
(default model + offered providers) on top of the #44 migration columns.

When a firm turns the policy OFF: members' personal model-preference writes
are gated (fixed 403), their stored prefs become inert (never deleted) and the
firm default governs server-side resolution, and the model pickers are filtered
to the firm's offered providers. Orgless self-hosters and policy-ON firms are
unchanged everywhere. All new columns read 42703-tolerantly; the gate and
resolver fail open on lookup error. No migration, no new env vars, no new deps.

Backend: organisations.ts (policy + modelConfig on membership,
getUserOrganisationModelContext, get/setOrganisationModelConfig,
normaliseModelConfig); llm/models.ts (MODEL_PROVIDERS, isSelectableModelId);
userSettings.ts + serializeProfile firm-aware resolution; requireMemberPolicy
gate on PATCH /user/profile (model fields only, admins not exempt); admin
PATCH /policies memberModelPrefs + GET/PATCH /admin/model-config with 400 on
unknown model/provider.

Frontend: mikeApi types + get/updateFirmModelConfig; firmPolicy helpers;
account/models neutral card under policy-off + offered-provider filtering;
ModelToggle offeredProviders prop (chat + tabular); firm-settings third policy
toggle + Model configuration card.

Tests: 295 backend vitest pass (24 files) incl. new userSettings.test.ts and
extended auth.policy/organisations/user.serialize/admin suites. Frontend tsc
clean; eslint no new problems on changed files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3fa1d9eb WS8 PR F review fix: enforce firm model policy on the chat routes sloth-ninja 2026-07-27 ↗ GitHub
commit body
Blocker from independent review: firm model config was filtered client-side
only. POST /chat and POST /projects/:id/chat accepted a raw body.model, so a
policy-OFF member (or a policy-ON member under a provider restriction) could
bypass the policy with a crafted request ("gate the routes, not the tabs").

Fix: new resolveOrgChatModel(userId, requested, db) in userSettings.ts, applied
right after model resolution on both streaming chat routes. Rules: local ids
pass through untouched; fail-open on org-lookup error / orgless; policy-OFF +
firm default -> forced to firm default; requested provider outside a non-empty
offeredProviders -> substituted (firm default -> default main model -> first
offered provider's default). models.ts gains safeProviderForModel +
defaultMainModelForProvider.

Coherence: PATCH /admin/model-config now rejects (400) a MERGED config whose
cloud default model's provider sits outside a non-empty offeredProviders, so the
clamp can treat a set firm default as an in-set substitute; a local default is
exempt.

Routes checked for raw client model ids: tabular resolves via getUserModelSettings
(already firm-aware); workflows accepts no client model - neither needs a clamp.

Tests: +14 (309 total, 24 files) - resolveOrgChatModel clamp matrix and the
model-config coherence 400s. Backend tsc + vitest green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6e84ad9b Merge pull request #45 from Sloth-ninja/ws8-firm-model-prefs Ezana.H 2026-07-27 ↗ GitHub
WS8 PR F: firm model preferences

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

⬇ Download capture-thread-868.md