mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add personal skill storage, API, and SDK (#25363)
> Mux updated this PR on behalf of Mike. ## Stack Context This PR is the storage, permissions, API, and SDK layer for experimental personal skills. #25362 has landed on `main`, so this branch is restacked directly on `main`. Stack order: 1. #25363 storage, permissions, API, and SDK 2. #25365 API test coverage 3. #25366 chattool and chatd integration 4. #25066 settings UI and docs 5. #25386 personal skills slash menu ## What? Adds the `user_skills` database table, generated queries, RBAC resources and scopes, audit resource handling, experimental user-scoped CRUD endpoints, SDK types, and generated API/site types. Follow-up review and restack fixes: - Enforce a bounded personal skill description in parser and database constraints. - Return `403 Forbidden` for unauthorized create and update attempts. - Return explicit conflict responses when soft-deleted users are targeted. - Keep user admins out of personal skills, while site owners can read and delete but not create or update. - Document trigger-raised constraint names and keep schema constants covered by tests. - Reuse `UserSkillMetadata` in the full `UserSkill` SDK response type. - Generate user skill IDs in Go instead of relying on a database default. - Rebase on latest `main` and renumber the user skills migration to `000502_user_skills`. ## Why? Personal skills need durable user-owned storage with owner authorization, limited site-owner moderation, and a hidden API surface before chatd can consume them. ## Validation - `make gen` - `go test ./coderd/database -run '^TestUserSkillSchemaConstants$' -count=1` - `go test ./coderd/database/dbauthz -run '^TestMethodTestSuite/TestUserSkills$' -count=1` - `go test ./coderd -run '^TestPatchUserSkill$' -count=1` - `go test ./codersdk ./coderd/database/db2sdk` - `make lint` - pre-commit hook on `97fd58108d`
This commit is contained in:
@@ -390,6 +390,29 @@ func Test_diff(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
runDiffTests(t, []diffTest{
|
||||
{
|
||||
// User skill content is user-authored instruction text, not secret
|
||||
// material, so audit diffs can include the content change.
|
||||
name: "UserSkillContentTracked",
|
||||
left: audit.Empty[database.UserSkill](),
|
||||
right: database.UserSkill{
|
||||
ID: uuid.UUID{1},
|
||||
UserID: uuid.UUID{2},
|
||||
Name: "review-guidance",
|
||||
Description: "How to review private projects",
|
||||
Content: "review markdown",
|
||||
},
|
||||
exp: audit.Map{
|
||||
"id": audit.OldNew{Old: "", New: uuid.UUID{1}.String()},
|
||||
"user_id": audit.OldNew{Old: "", New: uuid.UUID{2}.String()},
|
||||
"name": audit.OldNew{Old: "", New: "review-guidance"},
|
||||
"description": audit.OldNew{Old: "", New: "How to review private projects"},
|
||||
"content": audit.OldNew{Old: "", New: "review markdown"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
runDiffTests(t, []diffTest{
|
||||
{
|
||||
name: "Create",
|
||||
|
||||
@@ -34,6 +34,7 @@ var AuditActionMap = map[string][]codersdk.AuditAction{
|
||||
"AuditableGroupAiBudget": {codersdk.AuditActionWrite, codersdk.AuditActionDelete},
|
||||
"Chat": {codersdk.AuditActionCreate, codersdk.AuditActionWrite}, // chats get 'archived' by users, not deleted.
|
||||
"UserSecret": {codersdk.AuditActionCreate, codersdk.AuditActionWrite, codersdk.AuditActionDelete},
|
||||
"UserSkill": {codersdk.AuditActionCreate, codersdk.AuditActionWrite, codersdk.AuditActionDelete},
|
||||
}
|
||||
|
||||
type Action string
|
||||
@@ -446,6 +447,15 @@ var auditableResourcesTypes = map[any]map[string]Action{
|
||||
"plan_mode": ActionIgnore, // Can flip back and forth during a session.
|
||||
"client_type": ActionIgnore, // Set at creation.
|
||||
},
|
||||
&database.UserSkill{}: {
|
||||
"id": ActionTrack,
|
||||
"user_id": ActionTrack,
|
||||
"name": ActionTrack,
|
||||
"description": ActionTrack,
|
||||
"content": ActionTrack,
|
||||
"created_at": ActionIgnore,
|
||||
"updated_at": ActionIgnore,
|
||||
},
|
||||
&database.UserSecret{}: {
|
||||
"id": ActionTrack,
|
||||
"user_id": ActionTrack,
|
||||
|
||||
Reference in New Issue
Block a user