Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
e6b20a9
Update schema to support the foundation for skills, both personal and…
jsourcebot Jun 9, 2026
6720a87
Add initial UI and actions to support personal skills
jsourcebot Jun 9, 2026
67d0afb
support unsaved changes loss guard, work in progress
jsourcebot Jun 13, 2026
a36ae18
Add Ask command suggestions for personal skills
jsourcebot Jun 15, 2026
7df801a
feat: add slash command argument substitution
jsourcebot Jun 16, 2026
54d7750
refactor: model agent skill scope explicitly
jsourcebot Jun 17, 2026
13e9e03
Add org skill adoption commands
jsourcebot Jun 17, 2026
199d2e9
Add org skill settings UI
jsourcebot Jun 17, 2026
7e27ead
Add workspace skill management UI
jsourcebot Jun 18, 2026
15f5546
Add file mentions to skill instructions
jsourcebot Jun 20, 2026
0893716
feat(web): auto-invoke agent skills in Ask via load_skill tool
jsourcebot Jun 23, 2026
6e87376
feat(web): render load_skill tool calls and harden auto-invocation
jsourcebot Jun 24, 2026
bcbd0bf
refactor(web): force skill auto-invocation, drop arguments, org-scope…
jsourcebot Jun 25, 2026
0bc199a
refactor(web): move Ask Sourcebot skills into a standalone Skills page
jsourcebot Jun 25, 2026
2e3df3e
update on skills editing ux
jsourcebot Jun 25, 2026
71861cf
refactor(web): UI improvements for the Ask skills page
jsourcebot Jun 26, 2026
57a2641
refactor(web): remove the featured skills concept
jsourcebot Jun 26, 2026
24398b1
fix(web): only show search mode tooltips when the dropdown is open
jsourcebot Jun 27, 2026
000e05c
feat(web): import skills from markdown on the Skills page
jsourcebot Jun 27, 2026
1487fa1
feat(web): import skills from an indexed repository
jsourcebot Jun 27, 2026
00ef1ca
feat(web): repo-synced agent skills with org sharing and access gating
jsourcebot Jun 28, 2026
20df848
feat(web): overhaul shared-skills admin table and sharing UX
jsourcebot Jul 1, 2026
bd2ce09
refactor(web): drop dead skill toggle and redundant delete guard
jsourcebot Jul 1, 2026
da8eda7
Add changelog entry for Ask skills
jsourcebot Jul 2, 2026
688c2d3
chore: regenerate OpenAPI spec
github-actions[bot] Jul 2, 2026
a97d4b3
fix: stabilize workspace skills management
jsourcebot Jul 3, 2026
9d8b7e2
fix: refresh skills settings after mutations
jsourcebot Jul 3, 2026
517eb70
Squash agent skill migrations
jsourcebot Jul 6, 2026
4c9bac3
fix: escape agent skill catalog fields
jsourcebot Jul 7, 2026
a671233
fix(web): preserve manual skill invocation source
jsourcebot Jul 7, 2026
65e914f
fix: remove made-personal skills from shared catalog
jsourcebot Jul 7, 2026
f217135
fix(web): make unsaved changes bypass one-shot
jsourcebot Jul 7, 2026
adfd815
fix: center import repo dialog close button
jsourcebot Jul 7, 2026
8e133b4
Track ask skill analytics events
jsourcebot Jul 7, 2026
bda4bcc
Strengthen skill analytics redaction test
jsourcebot Jul 7, 2026
1d07eb9
Address ask skill analytics feedback
jsourcebot Jul 7, 2026
b293ee3
Address skill analytics feedback follow-ups
jsourcebot Jul 7, 2026
5572678
Rename agent skills migration
jsourcebot Jul 7, 2026
6e5a184
Improve skills editor guidance
jsourcebot Jul 8, 2026
e434cd6
Extract chat agent tools
jsourcebot Jul 9, 2026
f7e0dda
Rename skill command server helper
jsourcebot Jul 9, 2026
cf4a168
Refactor skills page components
jsourcebot Jul 9, 2026
8cc4193
Remove redundant tooltip portal wrappers
jsourcebot Jul 9, 2026
ac8a9d8
Add client validation for skill forms
jsourcebot Jul 9, 2026
116fe76
Extract shared chat mention chip
jsourcebot Jul 9, 2026
9643b82
Make synced skills editable with warn-on-overwrite sync
jsourcebot Jul 9, 2026
0133ae4
Add avatar to skill added-by metadata
jsourcebot Jul 9, 2026
9c31557
docs: add skills documentation images
jsourcebot Jul 9, 2026
7488cb9
Move skill field guidance into placeholders
jsourcebot Jul 9, 2026
2087cf4
docs: update skills page screenshot
jsourcebot Jul 10, 2026
566f2c7
fix(web): stabilize manual skill analytics
jsourcebot Jul 10, 2026
209fe90
refactor(web): standardize skills settings layout
jsourcebot Jul 10, 2026
4910aa5
fix(web): prevent duplicate skill saves
jsourcebot Jul 10, 2026
6bfde52
fix(web): scope shared skill sync status visibility
jsourcebot Jul 10, 2026
b698991
fix padding
brendan-kellam Jul 10, 2026
74c48b5
docs: link Ask skills changelog entry to PR
jsourcebot Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [EE] Added image attachments to Ask Sourcebot, letting users attach images to a chat message when the selected model supports image input. [#1375](https://github.com/sourcebot-dev/sourcebot/pull/1375)
- Added deployment system resource stats (CPU cores + cgroup quota, host + container memory, disk, load average) to the service ping, so resource issues can be diagnosed more quickly. [#1424](https://github.com/sourcebot-dev/sourcebot/pull/1424)
- Added a `robots.txt` that disallows crawlers, with an allowlist for link-preview bots so shared links keep their OpenGraph previews. [#1426](https://github.com/sourcebot-dev/sourcebot/pull/1426)
- [EE] Added Ask Sourcebot skills, letting users create, import, share, sync, and auto-invoke reusable chat instructions across personal and workspace scopes. [#1410](https://github.com/sourcebot-dev/sourcebot/pull/1410)

### Fixed
- Send anonymous server-side PostHog events as personless so unauthenticated requests don't inflate person counts. [#1367](https://github.com/sourcebot-dev/sourcebot/pull/1367)
Expand Down
3 changes: 3 additions & 0 deletions docs/api-reference/sourcebot-public.openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,9 @@
},
"externalWebUrl": {
"type": "string"
},
"blobSha": {
"type": "string"
}
},
"required": [
Expand Down
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"group": "Ask Sourcebot",
"root": "docs/features/ask/ask-sourcebot",
"pages": [
"docs/features/ask/skills",
"docs/features/ask/connectors",
"docs/features/ask/chat-sharing",
"docs/features/ask/add-model-providers"
Expand Down
130 changes: 130 additions & 0 deletions docs/docs/features/ask/skills.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
title: Skills
---

Skills are reusable context for [Ask Sourcebot](/docs/features/ask/ask-sourcebot).
They can capture organizational knowledge, define a workflow, or give Ask Sourcebot extra context for a class of questions.

<Frame>
<img src="/images/skills_page.png" alt="Skills page overview" />
</Frame>

## Creating a skill

Go to **Settings -> Skills** and click **Add skill**.

You can create a skill in three ways:

- **New skill**: Create a skill directly in Sourcebot.
- **Import from file**: Upload a local `.md` or `.markdown` file and review it before saving.
- **Import from repository**: Choose a markdown file from an indexed repository and keep the skill synced to that file.

Each skill has four fields:

- **Name**: A recognizable name for the skill.
- **Command**: The slash command users type to run the skill, such as `/deep-research`.
- **Description**: A short summary of when Ask Sourcebot should use the skill.
- **Instructions**: The markdown context or guidance Ask Sourcebot uses when the skill runs.

<Frame>
<img src="/images/skills_create.png" alt="Create a skill" />
</Frame>

## Writing instructions

Keep the description specific. Ask Sourcebot uses it to decide when a skill is relevant, and users see it when browsing slash commands.

Put the useful context or guidance in the instructions.

<Frame>
<img src="/images/skills_editor.png" alt="Skill editor" />
</Frame>

## Using skills in chat

Type `/` at the start of an Ask Sourcebot prompt to browse available skill commands.

When you select a skill, Ask Sourcebot expands the command into that skill's instructions before answering. If the instructions include file mentions, those files are added to the chat context automatically.

Ask Sourcebot may also load a matching skill automatically. When this happens, the chat details show the loaded skill name and command.

<Frame>
<img src="/images/skills_slash_command.png" alt="Skill slash command suggestions" />
</Frame>

## Personal and shared skills

Skills can be personal or shared:

- **Personal skills** are private to you in the current workspace.
- **Shared skills** are published to the workspace skill catalog.

You can add or remove shared skills from your own Ask commands from **Settings -> Skills**. Skill authors and organization owners can edit or delete shared skills.

Organization owners can manage shared skills from **Settings -> Workspace -> Ask Sourcebot**. Owners can also enable **Auto** for a shared skill, which adds it to members' Ask commands by default.

<Frame>
<img src="/images/skills_workspace_manager.png" alt="Workspace shared skills manager" />
</Frame>

## Importing skills

### Import from file

File imports support `.md` and `.markdown` files. If the file has front matter, Sourcebot uses it to prefill the skill name, command, and description.

Supported front matter fields:

```md
---
name: Debug Build Failure
slug: debug-build
description: Investigate a failing CI or build error.
---
```

You can also use `title` instead of `name`, and `command` instead of `slug`.

After importing a local file, the skill is editable in Sourcebot.

### Import from repository

Repository imports let you choose a markdown file from an indexed repository. Sourcebot prefills the create form from the file so you can review it, add a missing description, and save. The resulting skill stays synced to that file.

Synced skills behave like manually-created skills, with a link back to their source file:

- Every field stays editable. Local edits are kept until you sync.
- Sourcebot checks whether the indexed source file has changed.
- When an update is available, use **Update from source** to replace the skill's content with the file's.
- When the skill is already up to date, use **Force sync** to restore the content from the source file.
- Either action warns you first if it would overwrite local edits.
- Fields the source file does not provide (for example, a missing `description`) keep their local values when you sync.

<Frame>
<img src="/images/skills_synced_from_repo.png" alt="Synced repository skill" />
</Frame>

## Permissions and synced skills

Synced skills mirror content from a repository file, so Sourcebot only shows them to users who can access the source repository.

If repository permission syncing is enabled, sharing a synced skill shows a warning before publishing it to the workspace. Members without access to the source repository will not see the skill.

Organization owners can still see and manage shared synced skills from workspace settings, even if they do not have access to the source repository.

## Managing skills

From **Settings -> Skills**, you can:

- Search personal and shared skills.
- Create, edit, delete, or share skills.
- Add or remove shared skills from your commands.
- Make a shared skill personal if you manage it.
- Update synced skills from their source file.

Synced skills show their source repository and file path, plus one of these statuses:

- **Up to date**: The skill matches the indexed source file.
- **Update available**: The indexed source file has changed.
- **Source file not found**: The file was renamed, deleted, or no longer resolves at the imported revision.
- **Source repo unavailable**: The repository is no longer available or visible.
Binary file added docs/images/skills_create.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/skills_editor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/skills_page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/skills_slash_command.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/skills_synced_from_repo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/skills_workspace_manager.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Comment thread
brendan-kellam marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
-- CreateEnum
CREATE TYPE "AgentSkillVisibility" AS ENUM ('PERSONAL', 'SHARED');

-- CreateTable
CREATE TABLE "AgentSkill" (
"id" TEXT NOT NULL,
"visibility" "AgentSkillVisibility" NOT NULL,
"scopeId" TEXT NOT NULL,
"slug" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT NOT NULL,
"instructions" TEXT NOT NULL,
"createdById" TEXT NOT NULL,
"updatedById" TEXT,
"orgId" INTEGER NOT NULL,
"enabled" BOOLEAN NOT NULL DEFAULT true,
"autoEnrolled" BOOLEAN NOT NULL DEFAULT false,
"sourceRepoName" TEXT,
"sourceFilePath" TEXT,
"sourceRevision" TEXT,
"sourceBlobSha" TEXT,
"sourceImportedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "AgentSkill_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "AgentSkillAdoption" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"orgId" INTEGER NOT NULL,
"agentSkillId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"removedAt" TIMESTAMP(3),

CONSTRAINT "AgentSkillAdoption_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE INDEX "AgentSkill_createdById_idx" ON "AgentSkill"("createdById");

-- CreateIndex
CREATE INDEX "AgentSkill_updatedById_idx" ON "AgentSkill"("updatedById");

-- CreateIndex
CREATE INDEX "AgentSkill_orgId_idx" ON "AgentSkill"("orgId");

-- CreateIndex
CREATE INDEX "AgentSkill_orgId_visibility_scopeId_idx" ON "AgentSkill"("orgId", "visibility", "scopeId");

-- CreateIndex
CREATE INDEX "AgentSkill_shared_catalog_idx" ON "AgentSkill"("orgId", "visibility", "scopeId", "enabled", "updatedAt" DESC, "name");

-- CreateIndex
CREATE UNIQUE INDEX "AgentSkill_orgId_visibility_scopeId_slug_key" ON "AgentSkill"("orgId", "visibility", "scopeId", "slug");

-- CreateIndex
CREATE INDEX "AgentSkillAdoption_userId_orgId_removedAt_idx" ON "AgentSkillAdoption"("userId", "orgId", "removedAt");

-- CreateIndex
CREATE INDEX "AgentSkillAdoption_agentSkillId_idx" ON "AgentSkillAdoption"("agentSkillId");

-- CreateIndex
CREATE INDEX "AgentSkillAdoption_orgId_idx" ON "AgentSkillAdoption"("orgId");

-- CreateIndex
CREATE UNIQUE INDEX "AgentSkillAdoption_orgId_userId_agentSkillId_key" ON "AgentSkillAdoption"("orgId", "userId", "agentSkillId");

-- AddForeignKey
ALTER TABLE "AgentSkill" ADD CONSTRAINT "AgentSkill_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "AgentSkill" ADD CONSTRAINT "AgentSkill_updatedById_fkey" FOREIGN KEY ("updatedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "AgentSkill" ADD CONSTRAINT "AgentSkill_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "Org"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "AgentSkillAdoption" ADD CONSTRAINT "AgentSkillAdoption_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "AgentSkillAdoption" ADD CONSTRAINT "AgentSkillAdoption_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "Org"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "AgentSkillAdoption" ADD CONSTRAINT "AgentSkillAdoption_agentSkillId_fkey" FOREIGN KEY ("agentSkillId") REFERENCES "AgentSkill"("id") ON DELETE CASCADE ON UPDATE CASCADE;
92 changes: 92 additions & 0 deletions packages/db/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ model Org {
repoVisits RepoVisit[]

mcpServers McpServer[]
agentSkills AgentSkill[]
agentSkillAdoptions AgentSkillAdoption[]

license License?
servicePingEvents ServicePingEvent[]
Expand Down Expand Up @@ -519,6 +521,9 @@ model User {
sessionVersion Int @default(0)

userMcpServers UserMcpServer[]
createdAgentSkills AgentSkill[] @relation("AgentSkillCreatedBy")
updatedAgentSkills AgentSkill[] @relation("AgentSkillUpdatedBy")
agentSkillAdoptions AgentSkillAdoption[]

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Expand Down Expand Up @@ -741,6 +746,93 @@ model ChatAccess {
@@unique([chatId, userId])
}

enum AgentSkillVisibility {
PERSONAL
SHARED
}

model AgentSkill {
id String @id @default(cuid())

// Namespace identity for slash-command uniqueness and lookup.
// This is not a foreign key and should not be used as an authorization check by itself.
//
// Every skill belongs to an org (orgId is always set).
// PERSONAL: scopeId is the user id; the skill is private to that user within the org.
// SHARED: scopeId is String(orgId); the skill is visible to the whole org (opt-in via adoptions).
//
// Future sharing/grants should treat this as the skill's home namespace, not as the ACL.
visibility AgentSkillVisibility
scopeId String
slug String

name String
description String
instructions String

// The user who authored the skill. This is audit metadata, separate from
// the namespace that owns the slash-command slug.
createdBy User @relation("AgentSkillCreatedBy", fields: [createdById], references: [id], onDelete: Cascade)
createdById String

updatedBy User? @relation("AgentSkillUpdatedBy", fields: [updatedById], references: [id], onDelete: SetNull)
updatedById String?

Comment on lines +773 to +780

@coderabbitai coderabbitai Bot Jul 2, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

createdBy cascade delete can silently wipe shared/org skills.

createdBy uses onDelete: Cascade (Line 775) while updatedBy uses onDelete: SetNull (Line 778) for what the comment (Lines 773-774) calls audit metadata, "separate from the namespace that owns the slash-command slug." For PERSONAL skills this is harmless since scopeId == createdById anyway. But for SHARED skills, deleting the creator's User row (e.g., offboarding an employee) cascades to delete the entire org-wide shared skill — along with every other user's AgentSkillAdoption rows for it — even though the skill is meant to persist as an org resource independent of its original author.

This contradicts the stated intent and risks unexpected data loss on a routine admin action (user removal).

🛠️ Suggested fix
-  createdBy   User   `@relation`("AgentSkillCreatedBy", fields: [createdById], references: [id], onDelete: Cascade)
-  createdById String
+  createdBy   User?   `@relation`("AgentSkillCreatedBy", fields: [createdById], references: [id], onDelete: SetNull)
+  createdById String?

Note: this requires updating all createdById: userId call sites and Pick<AgentSkill, ... "createdById" ...> mapping code (e.g. toSharedAgentSkillCatalogItem in types.ts) to tolerate null.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// The user who authored the skill. This is audit metadata, separate from
// the namespace that owns the slash-command slug.
createdBy User @relation("AgentSkillCreatedBy", fields: [createdById], references: [id], onDelete: Cascade)
createdById String
updatedBy User? @relation("AgentSkillUpdatedBy", fields: [updatedById], references: [id], onDelete: SetNull)
updatedById String?
// The user who authored the skill. This is audit metadata, separate from
// the namespace that owns the slash-command slug.
createdBy User? `@relation`("AgentSkillCreatedBy", fields: [createdById], references: [id], onDelete: SetNull)
createdById String?
updatedBy User? `@relation`("AgentSkillUpdatedBy", fields: [updatedById], references: [id], onDelete: SetNull)
updatedById String?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/db/prisma/schema.prisma` around lines 773 - 780, The AgentSkill
audit relation is too destructive: `createdBy` currently cascades on user
deletion, which can delete shared/org skills and their adopters. Update the
`AgentSkill` model’s `createdBy`/`createdById` relation in the Prisma schema to
stop using cascade delete and allow the creator reference to be cleared instead,
matching the intent of audit metadata. Then update any `createdById` callers and
mappings that assume a required user, including `toSharedAgentSkillCatalogItem`
and any `Pick<AgentSkill, ... "createdById" ...>` usages, so they handle a
nullable creator.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As of current this was actually intentional, but it's unclear if it should be this way or if a shared skill truly becomes an org resource (but then it's weird that the creator is the sole person who can then remove everybodies access to it).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsourcebot Thanks for the context — that's a fair tension to flag. A few ways I'd think about resolving the ambiguity, depending on which mental model you want for SHARED skills:

  1. Skill is an org resource, ownership decoupled from a single user — switch createdBy/createdById to SetNull (matching updatedBy), and introduce an explicit notion of "current owner/maintainer" that's independent from the original author (e.g., allow any org OWNER to delete/manage a SHARED skill, not just the creator). This is the cleanest mental model for "shared skills survive offboarding," but requires updating the delete/update authorization logic (currently gated to the creator in actions.ts) plus making createdById nullable everywhere it's consumed (e.g., toSharedAgentSkillCatalogItem).
  2. Keep cascade, but guard the destructive path — leave the schema as-is, but before deleting a User, check for SHARED skills they authored and either block the deletion or prompt to reassign/transfer ownership first. This avoids a schema/type migration but adds friction to user offboarding flows.
  3. Status quo — accept that removing a user prunes their shared skills too, treating "shared" as more like "shared while the author remains a member" rather than a durable org asset. Simplest, but as you noted, has surprising blast radius (also deletes all AgentSkillAdoption rows for every other member who adopted it).

Given this is a genuine product/design decision rather than a clear-cut bug, I'll leave it to you and the team to decide the intended semantics. If you'd like, I can open a follow-up issue to track "clarify/rework AgentSkill ownership semantics for org offboarding" so it doesn't get lost — just let me know.


✏️ Learnings added
Learnt from: jsourcebot
Repo: sourcebot-dev/sourcebot PR: 1410
File: packages/db/prisma/schema.prisma:773-780
Timestamp: 2026-07-02T02:23:27.352Z
Learning: In Sourcebot's Prisma schema (packages/db/prisma/schema.prisma), the `AgentSkill.createdBy` relation intentionally uses `onDelete: Cascade` (as of PR `#1410`), meaning deleting a `User` who authored a `SHARED` (org-wide) skill also deletes that skill and all associated `AgentSkillAdoption` rows for every other user who adopted it. The team (jsourcebot) has acknowledged this is a known tradeoff/open design question — it's unclear whether shared skills should become independent org resources decoupled from their creator (which would require reworking ownership/authorization, e.g., allowing any org owner to manage a shared skill) or whether the current author-tied-deletion behavior is intended. Do not flag this as a definitive bug in future reviews unless the schema or stated intent changes; treat it as an open question already known to the maintainers.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: fatmcgav
Repo: sourcebot-dev/sourcebot PR: 1143
File: packages/db/prisma/schema.prisma:640-660
Timestamp: 2026-04-22T07:09:48.693Z
Learning: In Sourcebot’s Prisma schema, treat junction/relationship tables (e.g., AgentConfigToRepo, AgentConfigToConnection) as single-tenant and do not require a denormalized `orgId` column for tenant isolation. Tenant isolation is enforced by foreign keys/cascades from `AgentConfig` (which contains `orgId`). When reviewing queries/relations involving these junction tables, avoid raising multi-tenant isolation concerns as long as access is gated through `AgentConfig` filtered by `orgId`.

// The owning org. Both PERSONAL and SHARED skills are scoped to an org, so a
// user's personal skills change when they switch orgs.
org Org @relation(fields: [orgId], references: [id], onDelete: Cascade)
orgId Int

enabled Boolean @default(true)
autoEnrolled Boolean @default(false)

// Provenance for skills imported from an indexed repository file. All null for
// skills created manually or imported from a local file. When set, the skill's
// content is a read-only mirror of the source file: edits happen in the repo and
// are pulled in via "Update from source". sourceBlobSha (the git blob OID at
// import) is the comparison key used to detect when the indexed file has changed.
// Sync is personal-only; publishing to shared snapshots the content and drops
// these fields.
sourceRepoName String?
sourceFilePath String?
sourceRevision String?
sourceBlobSha String?
sourceImportedAt DateTime?

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

adoptions AgentSkillAdoption[]

@@unique([orgId, visibility, scopeId, slug])
@@index([createdById])
@@index([updatedById])
@@index([orgId])
@@index([orgId, visibility, scopeId])
@@index([orgId, visibility, scopeId, enabled, updatedAt(sort: Desc), name], map: "AgentSkill_shared_catalog_idx")
}

model AgentSkillAdoption {
id String @id @default(cuid())

user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String

org Org @relation(fields: [orgId], references: [id], onDelete: Cascade)
orgId Int

agentSkill AgentSkill @relation(fields: [agentSkillId], references: [id], onDelete: Cascade)
agentSkillId String

createdAt DateTime @default(now())
removedAt DateTime?

@@unique([orgId, userId, agentSkillId])
@@index([userId, orgId, removedAt])
@@index([agentSkillId])
@@index([orgId])
}

// OAuth2 Authorization Server models
// @see: https://datatracker.ietf.org/doc/html/rfc6749

Expand Down
Loading
Loading