fix: grant chatd ActionReadPersonal on User and parallelize runChat DB calls (#22970)

## Problem

1. **Personal behavior prompt not applied**: The chatd background worker
was missing `ActionReadPersonal` on `ResourceUser` in its RBAC subject.
When `resolveUserPrompt` calls `GetUserChatCustomPrompt`, the dbauthz
layer checks `ActionReadPersonal` on the user — which the chatd role
didn't have. The error was silently swallowed (returns `""`), so the
user's custom prompt was never injected into the system messages.

2. **Sequential DB calls on chat startup**: Several independent database
queries in `runChat` and `resolveChatModel` were running sequentially,
adding unnecessary latency before the LLM stream begins.

## Changes

### RBAC fix (`dbauthz.go`)
- Add `rbac.ResourceUser.Type: {policy.ActionReadPersonal}` to
`subjectChatd` site permissions
- This is the minimal permission needed — `ActionRead` on User remains
denied

### Parallelization (`chatd.go`)
Three parallelization points using `errgroup.Group`:

1. **`resolveChatModel`**: `resolveModelConfig` and
`GetEnabledChatProviders` run concurrently (both needed for
`ModelFromConfig`, which stays sequential after the wait)

2. **`runChat` startup**: `resolveChatModel` and
`GetChatMessagesForPromptByChatID` run concurrently (completely
independent)

3. **`runChat` prompt assembly**: `resolveInstructions` and
`resolveUserPrompt` run concurrently (both produce strings;
`InsertSystem` calls maintain correct order after the wait)

Same pattern applied to the `ReloadMessages` callback.

### Test (`dbauthz_test.go`)
- Add assertion in `TestAsChatd/AllowedActions` that
`ActionReadPersonal` on `ResourceUser` is permitted
This commit is contained in:
Kyle Carberry
2026-03-11 22:07:46 +00:00
committed by GitHub
parent 4d7eb2ae4b
commit 58f295059c
3 changed files with 90 additions and 31 deletions
+1
View File
@@ -707,6 +707,7 @@ var (
rbac.ResourceChat.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
rbac.ResourceWorkspace.Type: {policy.ActionRead},
rbac.ResourceDeploymentConfig.Type: {policy.ActionRead},
rbac.ResourceUser.Type: {policy.ActionReadPersonal},
}),
User: []rbac.Permission{},
ByOrgID: map[string]rbac.OrgPermissions{},
+4
View File
@@ -5459,6 +5459,10 @@ func TestAsChatd(t *testing.T) {
// DeploymentConfig read.
err = auth.Authorize(ctx, actor, policy.ActionRead, rbac.ResourceDeploymentConfig)
require.NoError(t, err, "deployment config read should be allowed")
// User read_personal (needed for GetUserChatCustomPrompt).
err = auth.Authorize(ctx, actor, policy.ActionReadPersonal, rbac.ResourceUser)
require.NoError(t, err, "user read_personal should be allowed")
})
t.Run("DeniedActions", func(t *testing.T) {