GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — feat: load organization skills in chats by ibetitsmike · Pull Request #30613 · coder/coder

Conversation

@ibetitsmike

@ibetitsmike ibetitsmike commented Oct 11, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

Stack

Closes CODAGT-1341. This stack adds organization skills to Coder Agents: admins publish a skill once per organization, share it with groups or members, and chats in that organization load it next to personal and workspace skills. It replaces the 21-PR draft stack #30584 to #30604 with five PRs, bottom to top:

  1. feat: add the organization skills data model #30609 data model: one migration, queries, RBAC and dbauthz
  2. feat: add organization skill API routes #30610 organization skill API routes and the scope-neutral SDK
  3. feat: add organization skill sharing ACL routes #30611 sharing ACL routes
  4. feat(site): add organization skill settings with a shared skills UI #30612 settings UI: shared skills table, admin page, member section
  5. feat: load organization skills in chats #30613 chats: chatd loading, / menu, docs (this PR)

What changes

This PR makes chats use organization skills: chatd adds the skills shared with the chat owner in the chat's organization to the skill index, read_skill loads them, the / menu offers them, and the docs describe the feature.

chatd (coderd/x/skills, coderd/x/chatd):

  • New source skills.SourceOrganization ("org"). MergeSkills(personal, organization, workspace) sorts by name, then source. A name in one source keeps its bare alias; a name in more than one source gets a qualified alias in every source that has it (personal/<name>, org/<name>, workspace/<name>), so no source overrides another. Lookup returns ErrSkillAmbiguous for a bare name that matches several sources.
  • Authorization (please review closely): each turn, chatd reads organization skill metadata and bodies as the chat owner's real RBAC subject with their roles and groups (chatOwnerSubject), never as the chatd system actor, so per-skill ACLs apply. Disabled skills are skipped. A skill that is deleted, disabled, or no longer shared by the time read_skill runs is reported as not found. If the owner subject or the metadata can't be loaded, turn preparation fails with the error, as main now does for personal skills (fix(coderd/x/chatd): return database errors from turn preparation #30492), instead of running the turn without organization skills.
  • read_skill loads personal and organization bodies through one LoadStoredSkillBody callback. An index description longer than 1,024 runes, the Agent Skills specification limit (maxSkillIndexDescriptionRunes), is cut to 1,023 runes plus an ellipsis so it stays within the limit; read_skill still returns the full skill.
  • Parse and lookup errors keep %w last on purpose: with xerrors, a %w that isn't last repeats the cause, so main's detail read "skill is too large: got N bytes, maximum is 65536 bytes: skill is too large". This stack returns the cause once (ThomasK33 asked about the order in review).

/ menu (site/src/pages/AgentsPage): the chat page fetches the chat organization's enabled skills (organizationSkills(...) from #30612's queries) next to personal ones. utils/skillAliases.ts (resolveSkillTriggers) follows MergeSkills: a personal or organization trigger is bare unless its name is in more than one source, then it becomes /personal/<name> or /org/<name>. While any list is unknown every trigger is qualified, because a qualified alias always resolves; workspace triggers are always qualified. Built-in /clear and /compact stay pending until every skill list is known, and a list with no data counts as unknown even after an error, so a failed fetch can't make a built-in run in place of a same-named skill. Disabled skills never shadow a built-in.

Docs: docs/ai-coder/agents/extending-agents.md gains two sections, Organization skills (manage them, control who can use one, find the ones you can use) and How chats load stored skills (turning a skill on or off, skills with the same name). The admin page's organization picker shows only for a user who can view more than one organization's skills. The member "From your organizations" section shows one organization at a time with its own picker, starts on the default organization, and has Export all for the selected organization. The Enabled column is a switch for editors and a check or X otherwise. The settings table in platform-controls/organizations.md gains rows for organization and personal skills.

Commits

The first commit reproduces the reviewed chatd code from the old stack; the site and docs commits reapply the old layers on #30612's queries, and the rest are labeled trims or fixes.

  • aa634eec94a feat(coderd/x): load organization skills in chats. Reproduction of the old skill sources and chatd layers (old head a1047f4986).
  • 97945db4e86 test(coderd/x/skills): make TestMergeSkills (6 cases) and TestLookup (8 cases) table-driven; coverage is 100% before and after, with the same blocks.
  • dde61cc5fe2 test(coderd/x/chatd): drop skill tests duplicated by the organization skills integration test TestOrganizationSkillsFollowChatOwnerAccess; the covered block set of the touched code is identical (348 of 506 blocks).
  • aad14b2dd05 feat(site/src/pages/AgentsPage): show organization skills in the slash menu. The old alias helper and slash menu layers, applied cleanly on feat(site): add organization skill settings with a shared skills UI #30612's queries.
  • 712e272e140 test(site/src/pages/AgentsPage): trim duplicated slash menu tests and stories. Deletes AgentChatPage.test.tsx (177 lines; its assertions live in submitChatTurn.test.ts and ChatMessageInput.test.tsx), two duplicate ChatMessageInput cases, six of 17 SkillsTriggerMenu stories, and one disabled-skill fixture row from a ChatMessageInput story.
  • 232535e1011 docs(docs/ai-coder/agents): document organization skills. From the old docs layer, with the View line updated for feat(site): add organization skill settings with a shared skills UI #30612's read-only dialog and Download.
  • 62dbbd2dfd3 refactor(site/src/pages/AgentsPage): address frontend review on the slash menu.
  • 7349eac4389 Merge the updated settings-ui layer into chats. Brings in the feat: add organization skill API routes #30610 and feat(site): add organization skill settings with a shared skills UI #30612 fixes; the merge has no changes of its own.
  • 0f84a30ec69 Merge the updated settings-ui layer into chats again, for feat(site): add organization skill settings with a shared skills UI #30612's read-only View fix; no changes of its own.
  • fb37822f985 fix(coderd/x/chatd/chattool): keep truncated skill index descriptions within 1,024 runes. The cut now happens at 1,023 runes so the ellipsis fits; the existing skill_test.go assertion now expects 1,023 runes plus the ellipsis.
  • 1543666b584 docs(docs/ai-coder/agents): state the 1,023-character cut before the skill index ellipsis.
  • 96f1da47c9a and 13f03d095ff merge the updated settings-ui layer for feat(site): add organization skill settings with a shared skills UI #30612's cache and Skills link fixes; no changes of their own.
  • b03c14c86de and 41cc078b85a merge the updated settings-ui layer; b03c14c86de moves the AgentChatPage.tsx and ChatMessageInput.tsx call sites, and their tests and stories, to feat(site): add organization skill settings with a shared skills UI #30612's split organizationSkills(...) query.
  • 1a32d05c9bc test(site/src/pages/AgentsPage/components/ChatMessageInput): restore per-source loading stories for the slash menu.
  • 830b0466982 fix(coderd/x/chatd): fail turn preparation when organization skills cannot load. Main now returns database errors from turn preparation (fix(coderd/x/chatd): return database errors from turn preparation #30492); organization skills still logged and dropped the failure, so a failed read silently hid them from the model. The new TestFetchOrganizationSkillMetadataListFailure covers the returned error.
  • 84c135f8801 and a6f8f63ad13 merge the updated settings-ui layer, which brings in main; they resolve generation_preparer.go against main's turn preparation error handling.
  • 19b7880840f merges feat(site): add organization skill settings with a shared skills UI #30612's member page test change; no changes of its own.
  • c74d597f3c5 merges feat(site): add organization skill settings with a shared skills UI #30612's read-only status change; no changes of its own.
  • f89c232c081 merges feat(site): add organization skill settings with a shared skills UI #30612's skills test changes; no changes of its own.
  • 17d5b9bfbc5 docs(docs/ai-coder/agents): describe the member skills organization picker and read-only Enabled status.
  • f97b1710f71 merges the updated settings-ui layer (main at e7b76872098); no changes of its own.

Size

Production +558/-213 (docs +116/-6), tests +895/-262; total +1453/-475. This is above the 500-line reviewer-friendly limit on purpose: Mike asked for 4 to 5 PRs instead of the 21 small ones this stack replaces.

Replaces

#30593 (skill sources), #30594 (chatd), #30601 (alias helper), #30602 (slash menu), #30604 (docs).

Validation

  • Go: all 21 packages under coderd/x/skills and coderd/x/chatd pass on 62dbbd2dfd3, plus the coverage comparisons above; coderd/x/chatd/chattool passes again on 13f03d095ff after the truncation fix.
  • Site: pnpm run lint and Biome clean; Vitest 102 tests; Storybook Vitest 49 + 4 tests. pnpm run lint reran clean on 13f03d095ff.
  • On a6f8f63ad13, this PR's head before the test-only 19b7880840f merge: go test passes for coderd/x/skills, coderd/x/chatd/chattool, and the coderd/x/chatd skill tests (-run 'OrganizationSkill|Skill'); 38 Vitest files covering skills and the slash menu pass, 542 tests with 2 skipped.
  • Docs: markdownlint 0 findings, Vale 0 findings.
  • On 17d5b9bfbc5: Vale clean, markdownlint and make pre-commit pass. On f97b1710f71: go build, the coderd/x/skills and chattool tests, pnpm check and tsc pass.
  • Remote UAT round 7 through Coder Agents: PASS (chat).
    • Full run on 62dbbd2dfd3, including an in-place upgrade from main 50c785cc6d7: personal skills kept their IDs, names, content hashes and timestamps, came up enabled, and worked in chats (read_skill) and the / menu; create, edit, download and delete worked after the upgrade. The admin page, sharing, the Enabled toggle, the member section, the / menu, chats reading organization skills as the owner, limits, audit, authorization and scoped keys, and the docs all passed.
    • Delta run on 7349eac4389: an update-only key gets 404 on PATCH with no content in the response; saving permissions refreshes Updated without a reload; smoke of the admin page, auditor View and Download, the / menu and read_skill.
    • Delta run on 0f84a30ec69 for aaa356f0fd5: read-only View, as an org auditor and as a member, shows no Name hint, focuses no field and starts on Close, and Escape or Close returns focus to the row menu button; Add skill and Edit still show the hint and focus the import field, and a saved edit persisted; smoke of the admin page, the / menu organization group and read_skill. Screen-reader behavior was not tested.
    • Delta run on 13f03d095ff for 698dd83946d, f82573cd1c2, 0e55137de7b and fb37822f985: for an organization admin of org B who is a plain member of org A, the sidebar Skills link drops ?org= for org A on seven AI settings pages and opens org B's skills, keeps ?org= for org B, and a typed /ai/settings/skills?org= for org A still shows the permission error. A skill deleted and recreated with the same name loads a fresh sharing list showing only Everyone, with no stale grant. A 1,500-character description appears in the chat's skill index as 1,023 characters plus an ellipsis, while read_skill returns all of it. Smoke of the admin toggle, the member section, the / menu organization group and read_skill.
    • Low findings: the read-only View hint and field focus, fixed in aaa356f0fd5 and verified by the delta run above. The member section's always-on disabled Enabled switch (also in the old stack) is replaced by a read-only status icon in feat(site): add organization skill settings with a shared skills UI #30612's 63bb42754e3, which has no UAT run. Not fixed: / shows the /personal/ prefix while organization skills fail to load (the unknown-list rule above); the shared Switch component's disabled styling (unchanged from main). A user who can read skills in only one organization sees no organization name on the Skills page, because the picker is hidden for a single organization, as on the MCP servers page on main.
    • Not tested: the workspace skills group (no running agent on the test deployment), the 100-per-user personal cap, and behavior without a Premium license.
  • Remote UAT round 8 through Coder Agents: PASS on b03c14c86de (same chat). The remote grader marked the unknown read_skill item FAIL only because the prompt asked for the literal phrase "skill not found"; the result names the skill once and says "not found" once, the same format as main, so the runner graded it PASS on intent.
    • Member page: starts on the default organization, shows one organization's table with the read-only note once, Export all downloads only the selected organization's skills, the picker is hidden with one organization, and an organization without enabled skills isn't listed.
    • Personal and admin tables: create, edit, toggle and delete each refetch the list; reopening the editor shows the new content even with the detail request delayed; a toggle changes state once, with no flicker. A skill deleted and recreated with the same name shows only Everyone in sharing.
    • The sidebar Skills link keeps ?org= only for an organization whose skills the user can read. A create over 64 KiB returns 400 with the detail "got 70000 bytes, maximum is 65536 bytes: skill is too large", the cause once. The / menu shows Commands, Personal skills and Organization skills, and invoking an organization skill loads its body through read_skill.
    • New low findings, not fixed: the member page picker's listbox is labeled "Suggestions" (likely from the shared OrganizationAutocomplete); a typed body over 64 KiB shows only the size counter and a disabled Create until Import; /ai/settings?org= redirects to the models page without ?org=. The first and third were not checked against main.
    • 9002dc60382, 1a32d05c9bc, 830b0466982, ac3e62c83b7, 63bb42754e3, 82b2487ad17, 17d5b9bfbc5 and the main merges came after b03c14c86de; they have tests but no UAT run.
    • Every UAT round ran the migration as 000615_skills. The heads ship the same up and down SQL as 000621_skills, so the upgrade path from main's 000620 to 000621 has no UAT run.

Xum opened this PR on behalf of @ibetitsmike.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
ibetitsmike added 30 commits October 10, 2026 20:44
@ibetitsmike
feat: add the organization skills data model
baa3150
Rename user_skills to skills and add organization and project owners, an
enabled flag, and per-skill user and group ACL columns in one migration.
Add the organization_skill RBAC resource, scopes, audit table entry, skill
queries, and dbauthz methods. The personal skills API, SDK, and frontend
are unchanged.
@ibetitsmike
test(coderd/database): keep only the SQL paths of the org skill matrix
1b84e83
TestOrganizationSkillAuthorization becomes TestOrganizationSkillQueries.
Coverage showed it is the only PR 1 test that runs the SQL-filtered list
with rows (the regosql converter and the row scan of
GetAuthorizedOrganizationSkillMetadata) and the only one that executes
UpdateOrganizationSkillByOrganizationIDAndName and
DeleteOrganizationSkillByOrganizationIDAndName, so those stay.

Removed cases and what covers them:
- Member, OrgAuditor, OtherOrgMember list cases: same SQL predicates as
  GroupMember, GrantedMember, OrgAdmin and GrantedOutside.
- Single-row get checks: TestRolePermissions OrganizationSkill* and
  TestSkillRBACObjectOrganizationACL decide the same Rego rules, and the
  dbauthz method suite asserts the fetch authorizes read on the row.
- Read-grant write denials: TestRolePermissions
  OrganizationSkillUserACLWrite and TestSkillRBACObjectOrganizationACL.
@ibetitsmike
test(coderd/database): fold the skill cap cases into existing coverage
e2931b4
The user branch of enforce_skills_per_owner_limit is already covered by
TestUserSkillLimit and TestUserSkillLimitConcurrentCreates in coderd,
which assert the skills_per_user_limit mapping to 409. The organization
case repeated ConcurrentOrganizationInsertsHonorCap, so that test now
also proves disabled rows count toward the cap and that another
organization is unaffected. Only the project branch keeps its own case.
@ibetitsmike
test(coderd/rbac): drop the organization skill read scope test
be62aea
TestExternalScopeNames already expands every catalog entry, including
organization_skill:read, to its single site permission, and allowing a
site scope permission on an organization object is generic Authorize
behavior. The catalog entry itself is pinned by the generated
codersdk/apikey_scopes_gen.go: removing it changes the generator output.
@ibetitsmike
feat: add organization skill API routes
49cb2c8
Make the skill SDK types scope-neutral, return enabled on skill
responses, and accept PATCH {content?, enabled?} for personal and
organization skills. Add organization skill CRUD routes under
/api/experimental/organizations/{organization}/skills with a param
middleware and an audit wrapper type.

API contract change: skill responses gain enabled, and the update
request's content becomes optional.
@ibetitsmike
refactor(coderd): share the skill create and update path across owners
dc893bc
@ibetitsmike
test(coderd): merge duplicate personal and organization skill tests
f949254
@ibetitsmike
feat: add organization skill sharing ACL routes
b1c8556
Add per-skill user and group ACL queries and GET/PATCH
/api/experimental/organizations/{organization}/skills/{skill}/acl plus
GET .../acl/available. Extract the MCP server ACL helpers into
coderd/mcp_acl.go so both resources share validation. The PATCH locks
the granted users' organization memberships before the skill row, which
matches the member-removal trigger's lock order.
@ibetitsmike
test(coderd): merge duplicate organization skill ACL route tests
b5d2ee2
The ACL handlers share their validation, canonicalization and candidate
listing helpers in coderd/mcp_acl.go with MCP server configs, and
TestMCPServerConfigACL and TestMCPServerConfigACLAvailable already cover
every block of those helpers. Role coverage of the share action lives in
TestRolePermissions.

So the site owner sharer case, the org auditor non-sharer case, the
foreign user, foreign group and duplicate spelling rejections, and the
separate available-candidates subtest go. The org admin subtest now also
checks that a user from another organization is not a candidate, which
fails if the handler passes the wrong organization. The unsupported role
rejection, sparse updates with user and group grants, scoped API keys,
the concurrent delete, and the member removal lock order tests stay.
@ibetitsmike
refactor(site/src/pages/AISettingsPage): share the ACL principal auto…
6463992
…complete and sharing helpers
@ibetitsmike
refactor(site/src): move the personal skills files to shared skills p…
e18599f
…aths
@ibetitsmike
refactor(site/src/pages/AgentsPage): drop the personal prefix from sh…
3c71e6c
…ared skill symbols
@ibetitsmike
refactor(site/src): key the skills table and queries by owner
3be35bc
@ibetitsmike
fix(site/src/pages/AgentsPage/components): return focus to the opener…
d0c1dfc
… when a skills dialog closes
@ibetitsmike
feat(site/src/pages/AgentsPage/components): add an Enabled toggle to …
92ef635
…the skills table
@ibetitsmike
refactor(site/src/pages/AgentsPage/components): move the skill edit a…
31c103e
…nd delete dialogs to their own file
@ibetitsmike
feat(site/src/pages/AgentsPage/components): view a skill read-only wh…
cff0bef
…en the user cannot edit it
@ibetitsmike
feat(site): add the organization skills admin page with sharing
a8b54a2
@ibetitsmike
feat(site/src): show organization skills on the personal skills page
391401b
@ibetitsmike
refactor(site/src/pages/AgentsPage): keep the skill dialogs in the ta…
2cf6e25
…ble view and extract focus return
@ibetitsmike
test(site/src/pages/AISettingsPage/SkillsPage): keep only the role wi…
0af9345
…ring test for the skills page
@ibetitsmike
fix(site/src/api/queries): refetch the skill list after a create when…
8032c67
… no list is cached
@ibetitsmike
refactor(site/src): tidy skills copy, editor prop naming, and story f…
a52f9ba
…ixtures
@ibetitsmike
feat(coderd/x): load organization skills in chats
aa634ee
skills.MergeSkills merges personal, org, and workspace skills in that order and qualifies a name that appears in more than one source with personal/, org/, or workspace/. Lookup still rejects an ambiguous bare name. read_skill loads personal and org bodies through one source-aware loader, and the skill index truncates descriptions to 1,024 runes behind a source-neutral alias hint.

Each prepareGeneration step reads the enabled org skills of the chat's organization as the chat owner's real RBAC subject, so per-skill ACL grants, revocations, and disabled skills apply on the next step. Disabled personal skills are hidden from the index and read_skill.
@ibetitsmike
test(coderd/x/skills): make TestMergeSkills and TestLookup table-driven
97945db
Trim only. Each merge case now compares the full resolved list, and the qualified and ambiguous lookups move from the merge collision cases into TestLookup. Statement coverage of the package is unchanged (100%, identical covered blocks).
@ibetitsmike
test(coderd/x/chatd): drop skill tests duplicated by the org skills i…
dde61cc
…ntegration test

Trim only. TestOrganizationSkillsFollowChatOwnerAccess already proves, against the real database and dbauthz, that a disabled personal skill is left out of the index and that read_skill loads an org skill through the stored loader. Remove the mock disabled row from TestFetchPersonalSkillMetadata and TestReadSkillTool/OrganizationSkillUsesStoredLoader, and restore the original personal-only read_skill_file case. Coverage of the skill code paths is unchanged.
@ibetitsmike
feat(site/src/pages/AgentsPage): show organization skills in the slas…
aad14b2
…h menu
@ibetitsmike
test(site/src/pages/AgentsPage): trim duplicated slash menu tests and…
712e272
… stories
@ibetitsmike
docs(docs/ai-coder/agents): document organization skills
232535e
@ibetitsmike
refactor(site/src/pages/AgentsPage): address frontend review on the s…
62dbbd2
…lash menu
@ibetitsmike
Merge the updated settings-ui layer into chats
19b7880
@ibetitsmike

ibetitsmike commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

chatgpt-codex-connector[bot] reacted with eyes emoji
All reactions
  • 👀 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

1 similar comment
@ibetitsmike

ibetitsmike commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 13, 2026

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 19b7880840

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ibetitsmike added 2 commits October 13, 2026 12:20
@ibetitsmike
fix(site/src/pages/AgentsPage): show read-only skill status instead o…
63bb427
…f a disabled switch
@ibetitsmike
Merge the updated settings-ui layer into chats
c74d597
@ibetitsmike

ibetitsmike commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 13, 2026

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: c74d597f3c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ibetitsmike added 2 commits October 13, 2026 12:57
@ibetitsmike
test(site/src): address Codex review on skills tests
82b2487
@ibetitsmike
Merge the updated settings-ui layer into chats
f89c232
@ibetitsmike

ibetitsmike commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 13, 2026

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: f89c232c08

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ibetitsmike
docs(docs/ai-coder/agents): describe the member skills organization p…
17d5b9b
…icker and read-only Enabled status
ThomasK33
ThomasK33 approved these changes Oct 13, 2026
View reviewed changes
ibetitsmike added 5 commits October 13, 2026 14:45
@ibetitsmike
Merge main into data-model
5248d56
@ibetitsmike
Merge the updated data-model layer into org-api
18373ff
@ibetitsmike
Merge the updated org-api layer into sharing
c4d22c4
@ibetitsmike
Merge the updated sharing layer into settings-ui
658be8b
@ibetitsmike
Merge the updated settings-ui layer into chats
f97b171
@ibetitsmike

ibetitsmike commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

chatgpt-codex-connector[bot]
chatgpt-codex-connector Bot reviewed Oct 13, 2026
View reviewed changes

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f97b1710f7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread coderd/x/chatd/generation_preparer.go Resolved

Uh oh!

There was an error while loading. Please reload this page.

ibetitsmike added a commit that referenced this pull request Oct 13, 2026
@ibetitsmike
feat: add the organization skills data model (#30609)
b67587c
## Stack

Part of
[CODAGT-1341](https://linear.app/codercom/issue/CODAGT-1341/support-organization-level-skills-in-coder-agents).
This stack adds organization skills to Coder Agents: admins publish a
skill once per organization, share it with groups or members, and chats
in that organization load it next to personal and workspace skills. It
replaces the 21-PR draft stack #30584 to #30604 with five PRs, bottom to
top:

1. **#30609** data model: one migration, queries, RBAC and dbauthz (this
PR)
2. #30610 organization skill API routes and the scope-neutral SDK
3. #30611 sharing ACL routes
4. #30612 settings UI: shared skills table, admin page, member section
5. #30613 chats: chatd loading, `/` menu, docs

## What changes

This PR adds the data model and authorization for organization skills.
Personal skills keep working unchanged: `git diff origin/main --
codersdk site` shows only generated RBAC and API key scope additions
(`apikey_scopes_gen.go`, `rbacresources_gen.go`,
`rbacresourcesGenerated.ts`, the RBAC and scope unions in
`typesGenerated.ts`) and `codersdk.ResourceTypeOrganizationSkill` with
its friendly string. No personal route, SDK type or site component
changes, and `coderd/userskills_test.go` changes only by mechanical
constant renames.

**One migration, `000621_skills`** (please review it closely):

- renames `user_skills` to `skills` with its constraints, index and
triggers, and recreates the cap function and
`delete_deleted_user_resources`, whose PL/pgSQL bodies name the table;
- adds `organization_id` and `project_id` (`ON DELETE CASCADE`),
`enabled` (default true), and `group_acl` / `user_acl` (`jsonb`, default
`{}`), with CHECKs for exactly one owner, ACLs that are JSON objects,
and ACLs that stay empty unless an organization owns the row, plus
partial unique name indexes per owner kind;
- replaces the cap trigger with `enforce_skills_per_owner_limit()`: 100
rows per owner, disabled rows included, locking the owner row `FOR NO
KEY UPDATE` so unrelated foreign key checks are not blocked;
- adds a `BEFORE DELETE` trigger on `organization_members` that removes
a departing member from `user_acl` on that organization's skills, so
rejoining doesn't restore a direct grant;
- adds the `organization_skill` `resource_type` and `api_key_scope` enum
values last; nothing in the migration uses them.

The down migration undoes the three old migrations in reverse order: it
drops organization and project rows and restores `user_skills`, its
names, the cap function and `user_id NOT NULL`.

Authorization: a new RBAC resource `organization_skill` (create, read,
update, delete, share). Owner and org admin get it; site and org
auditors get read; members read only through the skill's ACL, which
applies only to members of its organization. `Skill.RBACObject()`
returns the organization object with both ACLs; project rows stay denied
to everyone. New sqlc queries cover organization insert, get, list
(SQL-filtered through `GetAuthorizedOrganizationSkillMetadata` and a
`regosql` converter), update and delete, with dbauthz wrappers and the
audit table entries. No routes yet.

## Commits

The first commit reproduces the reviewed code from the old stack; each
later commit is a labeled trim or fix.

- `baa3150e3b7` feat: add the organization skills data model.
Reproduction: every non-site file this commit changes equals the
reviewed old `authz` head `1e16b3004c4`, except that the old stack's
migrations 000615, 000616 and 000617 are merged into one migration (then
`000615_skills`, now `000621_skills`) with one fixture
(`testdata/fixtures/000621_skills.up.sql`) and one migration test (now
`testMigration000621Skills`). At that commit, `dump.sql` was
byte-identical to the old head's.
- `1b84e83463a` test(coderd/database): keep only the SQL paths of the
org skill matrix. `TestOrganizationSkillAuthorization` becomes
`TestOrganizationSkillQueries` instead of being deleted, because
coverage showed it is this PR's only run of the
`GetAuthorizedOrganizationSkillMetadata` row scan (80% to 65% without
it) and of the organization update and delete queries (100% to 0%).
Swapping `group_acl` and `user_acl` in the converter makes
`GrantedMember` fail. The removed cases are decided by
`TestRolePermissions`, `TestSkillRBACObjectOrganizationACL` and the
dbauthz method suite.
- `e2931b412e9` test(coderd/database): fold the skill cap cases into
existing coverage. The user case is covered by `TestUserSkillLimit` and
`TestUserSkillLimitConcurrentCreates`; the organization case folds into
`ConcurrentOrganizationInsertsHonorCap`, which now also checks that
disabled rows count and another organization is unaffected (mutation
checked); the project case stays.
- `be62aea9021` test(coderd/rbac): drop
`TestOrganizationSkillReadScope`, covered by `TestExternalScopeNames`
and the generated scope file.
- `0c275ba37de` merges main at `454a2fcc697`, which took 000615 to
000618: it renumbers the migration to 000619 and resolves `dump.sql`,
`check_constraint.go` and the generated API docs. `8f2b4b87ee2` merges
main at `ad85646164e`, which took 000619: it renumbers the migration to
000620.
- `5248d56b64f` merges main at `e7b76872098`, which took 000620
(`000620_audit_chat_project_instructions_resource_type`): it renumbers
the migration, fixture and migration test to `000621_skills` (step
`Migration000621Skills`, version 621), and resolves
`enterprise/audit/table.go` (keeping main's
`AuditableChatProjectInstructions` entries and the `Skill` entry),
`dump.sql` and `models.go` (the `resource_type` enum gets
`chat_project_instructions`, then `organization_skill`) and the
regenerated `schemas.md`. The up and down SQL are unchanged by every
renumbering.

## Size

Production +549/-67, tests +728/-33, generated +881/-314; total
+2158/-414. This is above the 500-line reviewer-friendly limit on
purpose: Mike asked for 4 to 5 PRs instead of the 21 small ones this
stack replaces.

## Replaces

#30584 (rename `user_skills` to `skills`), #30585 (skill owners), #30586
(organization skill authorization).

## Validation

- Migration test: up keeps the existing personal row and its ID; down
restores the `user_skills` names, cap function and `NOT NULL`, and drops
organization rows; soft-deleting a user removes their skills after up,
after down, and after re-up.
- `go test` passed on `be62aea9021`, before the main merges, for
`coderd` (skill tests), `coderd/database`, `dbauthz`, `migrations`,
`gentest`, `coderd/rbac/...`, `coderd/x/chatd`, `coderd/x/skills`,
`enterprise/audit` and `enterprise/coderd`; `make lint/check-scopes`
passed; the four personal skills Vitest files pass unchanged.
- On `5248d56b64f`: build, vet, `TestMigrate`, `TestMigrationChain`, the
`migrations`, `database`, `dbauthz`, `gentest`, `rbac` and
`enterprise/audit` packages pass; `make gen` run twice is clean, and
`make pre-commit` passes.
- Remote UAT round 7 through Coder Agents passed on the whole stack; see
#30613's Validation for scope and findings.

> Xum opened this PR on behalf of @ibetitsmike.
ibetitsmike added a commit that referenced this pull request Oct 13, 2026
@ibetitsmike
feat: add organization skill API routes (#30610)
db7f5a7
## Stack

Part of
[CODAGT-1341](https://linear.app/codercom/issue/CODAGT-1341/support-organization-level-skills-in-coder-agents).
This stack adds organization skills to Coder Agents: admins publish a
skill once per organization, share it with groups or members, and chats
in that organization load it next to personal and workspace skills. It
replaces the 21-PR draft stack #30584 to #30604 with five PRs, bottom to
top:

1. #30609 data model: one migration, queries, RBAC and dbauthz
2. **#30610** organization skill API routes and the scope-neutral SDK
(this PR)
3. #30611 sharing ACL routes
4. #30612 settings UI: shared skills table, admin page, member section
5. #30613 chats: chatd loading, `/` menu, docs

## What changes

This PR adds the organization skill CRUD routes and makes the skill SDK
types owner-neutral so personal and organization skills share them.

**API contract change (experimental API, personal and organization
skills):** skill list and get responses gain `enabled`, and `PATCH
.../skills/{skillName}` now takes `{content?, enabled?}`, so `content`
is optional and a toggle doesn't resend it. A PATCH with neither field
returns 400; `content`, when present, is validated as before. In the
SDK, `UserSkill`, `UserSkillMetadata`, `CreateUserSkillRequest` and
`UpdateUserSkillRequest` become `Skill`, `SkillMetadata`,
`CreateSkillRequest` and `UpdateSkillRequest` (`codersdk/userskills.go`
moves to `codersdk/skills.go`), and `UpdateSkillRequest.Content` is now
`*string`. The personal client method names are unchanged. The site
changes are the handwritten edits that rename forces (`api.ts`,
`queries/userSkills.ts`, `testHelpers/skills.ts` and three call sites),
with no UI change.

Organization routes under
`/api/experimental/organizations/{organization}/skills`, not gated by an
experiment or license, like the personal ones:

- `GET` lists with the caller's own permissions, so admins, the site
owner and org auditors see every skill and members see only skills
shared with them. `GET /{skillName}` goes through the new
`httpmw.ExtractOrganizationSkillParam` and admits only read, so a
missing skill and a denied one both return 404.
- `POST` checks `create` on the organization before reading the body,
then seeds `group_acl` with the Everyone group (whose ID is the
organization ID) so a new skill reaches every member until an admin
narrows it. The 100-skill cap and a duplicate name return 409.
- `PATCH` admits only callers who can read the skill, because its
response includes the content; `DELETE` admits callers who can read or
delete it. A reader who can't change the skill gets a write-specific 403
from the database wrapper, and everyone else gets 404. PATCH locks the
row as system, updates as the caller in the same transaction, and audits
the locked row only after the transaction commits.
- Audit: `database.AuditableOrganizationSkill` records resource type
`organization_skill` with the organization ID; personal skills keep
`user_skill`.

Personal and organization handlers share one create and update path in
`coderd/userskills.go` (`createSkill`, `updateSkill`, and a
`skillWriteErrors` table per owner and action).

## Commits

The first commit reproduces the reviewed code from the old stack; each
later commit is a labeled trim or fix.

- `49cb2c84276` feat: add organization skill API routes. Reproduction:
every file this commit changes, including the SDK layer's site edits,
equals the reviewed old `write-routes` head `aa597100f28`; the rest of
the tree differs only by #30609's migration merge and main.
- `dc893bc60d6` refactor(coderd): share the skill create and update path
across owners (production +164/-188). It keeps the organization create
check before the body read, the lock as system then update as caller
order, and audit assignment after the transaction. No swagger,
`codersdk` or `typesGenerated.ts` change; `make gen` is clean.
- `f94925425ef` test(coderd): merge duplicate personal and organization
skill tests. `TestSkillsCRUD`, `TestSkillValidationAndConflicts` and
`TestSkillAudit` are table-driven over Personal, OrgAdmin and SiteOwner,
alongside `TestOrganizationSkillAuditsDeniedWrites` and one
`ScopedAPIKeys` subtest; `TestPatchUserSkill` is deleted because none of
its blocks were unique. Coverage of `userskills.go`,
`organizationskills.go` and `httpmw/organizationskillparam.go` is 101 of
117 blocks before and after, the same block set.
- `ad0a7a37504` fix(coderd): require read access to patch an
organization skill. The organization PATCH route now admits only `read`,
because its response carries the skill's content: a key scoped to
`organization_skill:update` without read gets 404, like GET, instead of
200 with the content. Readers without update still get 403, and DELETE
is unchanged. `TestOrganizationSkillAccess/ScopedAPIKeys` asserts the
404, that the response has no content, and that the skill is unchanged;
against the old route it fails with 200.
- `69d00ded0b6` and `f67b79343ec` merge the updated #30609 layer,
including main; `69d00ded0b6` resolves a conflict in the personal skills
page with main. No other changes of their own.
- `18373ffd59e` merges the updated #30609 layer (main at `e7b76872098`)
and reapplies the `AuditableOrganizationSkill` entry in
`enterprise/audit/table.go` alongside main's new
`AuditableChatProjectInstructions` entries.

## Size

Production +838/-308, tests +733/-322, generated +801/-274; total
+2372/-904. This is above the 500-line reviewer-friendly limit on
purpose: Mike asked for 4 to 5 PRs instead of the 21 small ones this
stack replaces.

## Replaces

#30587 (scope-neutral SDK and personal enabled state), #30588 (read
routes), #30589 (write routes and audit).

## Validation

- The coverage comparison above, before and after the test merge.
- `make gen` leaves no diff after the shared create and update refactor.
- On `18373ffd59e`: `make gen` is clean; build, vet, the `coderd` and
`enterprise/coderd` skill tests, `rbac`, `httpmw`, `dbauthz` and
`enterprise/audit` pass; `pnpm check`, `pnpm lint` and `make pre-commit`
pass.
- Remote UAT round 7 through Coder Agents passed on the whole stack; see
#30613's Validation for scope and findings.

> Xum opened this PR on behalf of @ibetitsmike.
ibetitsmike added a commit that referenced this pull request Oct 13, 2026
@ibetitsmike
feat: add organization skill sharing ACL routes (#30611)
2c432a1
## Stack

Part of
[CODAGT-1341](https://linear.app/codercom/issue/CODAGT-1341/support-organization-level-skills-in-coder-agents).
This stack adds organization skills to Coder Agents: admins publish a
skill once per organization, share it with groups or members, and chats
in that organization load it next to personal and workspace skills. It
replaces the 21-PR draft stack #30584 to #30604 with five PRs, bottom to
top:

1. #30609 data model: one migration, queries, RBAC and dbauthz
2. #30610 organization skill API routes and the scope-neutral SDK
3. **#30611** sharing ACL routes (this PR)
4. #30612 settings UI: shared skills table, admin page, member section
5. #30613 chats: chatd loading, `/` menu, docs

## What changes

This PR adds the routes that let admins choose who can read an
organization skill. It reuses the MCP server ACL machinery rather than
adding a second implementation.

Routes under
`/api/experimental/organizations/{organization}/skills/{skillName}`, all
behind `httpmw.ExtractOrganizationSkillParam` with `share`, so a caller
without share gets 404:

- `GET /acl` returns the resolved user and group grants.
- `GET /acl/available` returns the organization's users and groups that
can be granted, with the same `q`, `after_id`, `limit` and `offset`
paging as the MCP endpoint.
- `PATCH /acl` takes a sparse `UpdateOrganizationSkillACLRequest`: only
the listed principals change, `read` grants and `""` removes. It returns
204 and writes a `write` audit entry with the old and new ACL.

**Concurrency (please review closely):** the PATCH transaction first
locks the granted users' `organization_members` rows `FOR KEY SHARE`
(new query `LockOrganizationMembersByUserIDsForShare`, run as system),
then locks the skill row `FOR UPDATE`, then validates the update on the
transaction handle. Member removal takes the same locks in the same
order (its membership row, then the skill rows through the #30609
trigger), so the two can't deadlock, and a removal can't miss a grant
written by a concurrent PATCH. A skill deleted between the middleware
read and the row lock stays a 404.

Grants are users and groups, the same as MCP server configs (Mike's
decision, for parity). Only the `read` role exists; any other role is a
validation error, as are users or groups outside the organization.

Supporting changes:

- `coderd/mcp_acl.go`: the MCP ACL handlers' resolution, candidate
listing and validation move into generic helpers
(`writeOrganizationACLAvailable`, `validateOrganizationACLUpdate`,
`resolveACLUsers`, `resolveACLGroups`) shared by both resources;
`acl.Validate` accepts a named `acl.Role` constraint that now includes
`codersdk.OrganizationSkillRole`.
- `codersdk/skills.go`: `OrganizationSkillRole`, `OrganizationSkillACL`
with its user and group entries, `UpdateOrganizationSkillACLRequest`,
and client methods.
- New sqlc query `UpdateOrganizationSkillACLByID` with its dbauthz
wrapper (requires `share`).

## Commits

The first commit reproduces the reviewed code from the old stack; each
later commit is a labeled trim or fix.

- `b1c85560b69` feat: add organization skill sharing ACL routes.
Reproduction: every file this commit changes equals the reviewed old
`acl-routes` head `80c1c3594e6`; the rest of the tree differs only by
#30609's migration merge and main.
- `b5d2ee265c8` test(coderd): merge duplicate organization skill ACL
route tests (+49/-111). The removed cases repeated
`TestMCPServerConfigACL`, `TestMCPServerConfigACLAvailable` or
`TestRolePermissions`; with the MCP tests included, the covered block
set of `organizationskills_acl.go`, `mcp_acl.go` and
`rbac/acl/updatevalidator.go` is identical before and after. Kept:
`TestOrganizationSkillACL` (`OrgAdminManagesACL`, `ReadersGetNotFound`,
`SparseUpdateChangesReaders` with the audit diff,
`RejectsUnsupportedRole`, `ScopedAPIKeys`),
`TestOrganizationSkillACLConcurrentDelete`, and the lock order tests
`TestOrganizationSkillACLMemberRemoval` (`RemovalBeforeGrant`,
`RemovalAfterGrant`). `OrgAdminManagesACL` now also checks that users
from another organization are not candidates (mutation checked). Sharing
the param extractor with MCP was skipped: it would save about 10 lines
of security-sensitive code.
- `e09c4dba66b` Merge the updated org-api layer into sharing. Brings in
#30610's `ad0a7a37504`; the merge has no changes of its own.
- `1d3817dc639` and `c8c88f84d18` merge the updated #30610 layer,
including main; no changes of their own.
- `c4d22c47990` merges the updated #30610 layer (main at `e7b76872098`);
no changes of its own.

## Size

Production +431/-102, tests +377/-0, generated +918/-0; total
+1726/-102. This is above the 500-line reviewer-friendly limit on
purpose: Mike asked for 4 to 5 PRs instead of the 21 small ones this
stack replaces.

## Replaces

#30591 (ACL queries), #30592 (ACL routes).

## Validation

- `go build` and `go vet` passed; `make gen` exited 0 with no diff.
- `go test` passed for `coderd` (skill, MCP and ACL tests),
`coderd/database`, `dbauthz`, `migrations`, `gentest`,
`coderd/rbac/...`, `coderd/httpmw`, `coderd/x/skills/...`,
`coderd/x/chatd/...`, and the audit and skill tests in
`enterprise/audit` and `enterprise/coderd`.
- The covered block comparison above, before and after the test merge.
- On `c4d22c47990`: the same checks as #30610's head, plus `go test
./coderd/ -run 'Skill|MCP|ACL'`, pass.
- Remote UAT round 7 through Coder Agents passed on the whole stack; see
#30613's Validation for scope and findings.

> Xum opened this PR on behalf of @ibetitsmike.
Base automatically changed from mike/skills/settings-ui to main October 13, 2026 16:57
ibetitsmike added a commit that referenced this pull request Oct 13, 2026
@ibetitsmike
feat(site): add organization skill settings with a shared skills UI (#…
6498c57
…30612)

## Stack

Part of
[CODAGT-1341](https://linear.app/codercom/issue/CODAGT-1341/support-organization-level-skills-in-coder-agents).
This stack adds organization skills to Coder Agents: admins publish a
skill once per organization, share it with groups or members, and chats
in that organization load it next to personal and workspace skills. It
replaces the 21-PR draft stack #30584 to #30604 with five PRs, bottom to
top:

1. #30609 data model: one migration, queries, RBAC and dbauthz
2. #30610 organization skill API routes and the scope-neutral SDK
3. #30611 sharing ACL routes
4. **#30612** settings UI: shared skills table, admin page, member
section (this PR)
5. #30613 chats: chatd loading, `/` menu, docs

## What changes

This PR turns the personal skills page into a skills table that works
for any owner, then uses it for an organization admin page and a
read-only member section. It changes only `site/`.

**Shared skills table.** The personal page's files move to owner-neutral
paths (kept as git renames, so the diff shows edits):
`AgentSettingsPersonalSkillsPageView.tsx` becomes
`components/SkillsTableView.tsx`, `AgentSettingsPersonalSkillsPage.tsx`
becomes `components/SkillsTable.tsx`, `PersonalSkillEditor` becomes
`SkillEditor`, and `utils/personalSkills` becomes `utils/skills`.
`queries/userSkills.ts` keeps main's query names and keys for personal
skills, and the new `queries/organizationSkills.ts` holds the
organization and ACL queries. The table takes a `SkillsScope` (list,
detail, create, update and delete queries), built by `userSkillsScope`
or `organizationSkillsScope` in `utils/skills.ts`, plus an `access`
object (`create`, `update`, `delete`) and its copy as props.

- **Enabled toggle:** `SkillEnabledSwitch` runs one update mutation per
row from the table's scope, is disabled while it is pending, and
invalidates the list when it settles, which also drops a skill deleted
elsewhere.
- **Read-only status:** a table without update access (the member
section, and auditors on the admin page) shows a green check ("Enabled")
or a gray cross ("Disabled") in the Enabled column instead of a disabled
switch. Editable tables keep the switch.
- **Read-only view:** when the caller can't update a skill, the row
opens `SkillEditor` in read-only mode with a Close button, replacing the
old View dialog. It omits the naming hint and starts focus on Close.
- Dialogs return focus to the control that opened them
(`useDialogFocusReturn`).
- Create, update and delete invalidate the skill list instead of
patching it by hand. Update also writes the PATCH response to the
skill's detail, so a reopened editor shows the new content.
- Saving an ACL refreshes the organization skill list and the skill's
detail. Deleting a skill removes its detail and the ACL queries cached
under it, so a skill recreated with the same name doesn't inherit them.

**Admin page** at `/ai/settings/skills?org=<name>`
(`AISettingsPage/SkillsPage`): lists, creates, edits, toggles and
deletes organization skills with the caller's per-organization
permissions, and opens `OrganizationSkillSharingDialog` for users with
share. A requested organization the caller can't read shows the
permission error instead of falling back to another organization, so a
skill is never added to an organization the URL didn't name. The new
`viewAnyOrganizationSkills` permission adds Skills to the AI settings
sidebar, shows the navbar's AI settings link, and lets the AI settings
index redirect to the skills page. The sidebar's Skills link keeps the
current `?org=` only when the user can read that organization's skills,
using the page's own filter (`readableSkillsOrganizations`), so
following the link never lands on the permission error.

**Member section.** The agent settings page becomes
`/agents/settings/skills` ("Skills" in the sidebar; the old
`/agents/settings/personal-skills` URL redirects). Below personal
skills, "From your organizations" shows one organization at a time,
read-only. Its picker lists the organizations the user is a member of
that have enabled skills for them (an organization still loading or
whose list failed stays listed), starts on the default organization, and
is hidden when only one organization qualifies. Export all downloads
only the selected organization's skills. If the selected organization's
list fails, a retry button (labeled "Retry loading _name_ skills")
replaces the table.

**Shared sharing helpers.** `ChatModelPrincipalAutocomplete` becomes
`components/ACLPrincipalAutocomplete`, used by the model, MCP server and
skill sharing dialogs; `MCPServerPrincipalAutocomplete` is deleted and
the common ACL diff logic moves to `aclSharing.ts`. A new
`ChatModelSharingDialog.test.tsx` guards the model dialog through the
swap.

Declined on purpose (Mike's call): extra toggle cache hardening, namely
a pending-toggle map, reconciling a View that 404s, and guarding a stale
refetch against a retried toggle. Invalidating on settle already
refetches the list after every toggle.

## Commits

This PR reworks the old site layers rather than reproducing them. The
commits go in review order: shared helpers, renames, the shared table,
the two pages, then review fixes.

1. `6463992b717` refactor: share the ACL principal autocomplete and
sharing helpers.
2. `e18599f5049` refactor: move the personal skills files to shared
skills paths (file moves plus import path updates only).
3. `3c71e6c2168` refactor: drop the personal prefix from shared skill
symbols.
4. `3be35bcde68` refactor: key the skills table and queries by owner.
5. `d0c1dfcfdd1` fix: return focus to the opener when a skills dialog
closes.
6. `92ef6351eb4` feat: add an Enabled toggle to the skills table.
7. `31c103e5396` refactor: move the skill edit and delete dialogs to
their own file.
8. `cff0bef4576` feat: view a skill read-only when the user cannot edit
it.
9. `a8b54a22981` feat: add the organization skills admin page with
sharing.
10. `391401b62b7` feat: show organization skills on the personal skills
page.
11. `2cf6e250a60` refactor: keep the skill dialogs in the table view and
extract focus return. This undoes the file split from commit 7 so
`SkillsTableView.tsx` still diffs as a rename (497 lines then, 507 at
the head).
12. `0af93452c7d` test: keep only the role wiring test for the skills
page.
13. `8032c6708a8` fix: refetch the skill list after a create when no
list is cached (superseded by item 22).
14. `a52f9ba659c` refactor: tidy skills copy, editor prop naming, and
story fixtures.
15. `f10bc316305` fix: refresh the organization skill list after saving
its ACL. Saving an ACL bumps the skill's `updated_at`, so the save now
also invalidates the organization skill list and the skill's detail; a
new test in `api/queries/skills.test.ts` (now
`organizationSkills.test.ts`) covers it.
16. `698a6d0b55f` Merge the updated sharing layer into settings-ui,
bringing in #30610's PATCH fix.
17. `aaa356f0fd5` fix: drop the editing hint and field focus from the
read-only skill view. From the remote UAT: read-only View no longer
shows the Name field's naming hint, and the dialog opens with focus on
Close instead of a field (`onOpenAutoFocus`, like `ChatSearchDialog`).
The new `SkillEditor.test.tsx` case "starts focus on Close when
read-only" fails without the fix.
18. `698dd83946d` fix: drop a deleted skill's ACL caches with its
detail. The delete now removes every query under the skill's key, not
only the exact detail; a new case in `api/queries/skills.test.ts` (now
`organizationSkills.test.ts`) covers it.
19. `f82573cd1c2` fix: carry only a skill-readable organization on the
Skills link, with a table-driven `AISettingsSidebar.test.tsx` case.
20. `0e55137de7b` fix: share the skill-readable organization filter
(`SkillsPage/readableSkillsOrganizations.ts`, used by the sidebar and
`SkillsPage`) and cover an organization that is listed but unreadable.
21. `97d42fd3cd8` refactor: split skill queries by owner.
`queries/skills.ts` goes back to main's `userSkills.ts` (main's names
and keys) plus a new `organizationSkills.ts` for the organization and
ACL queries; the table takes a `SkillsScope` instead of an owner union.
22. `4ddfb80ac54` refactor: invalidate skill lists after writes, instead
of patching the cached list; update still writes the detail from the
PATCH response.
23. `14d5b755087` refactor: pass skill organizations to the sidebar as
`Organization` objects instead of names.
24. `463df6a5e69` feat: pick one organization on the member skills page.
25. `9c802557b9c` refactor: tighten the skill query comments and tests.
26. `9002dc60382` test: cover the failed sharing save in Vitest
(`OrganizationSkillSharingDialog.test.tsx`).
27. `a9df756b976` and `5ebf7b8984a` merge the updated sharing layer,
which brings in main. `a9df756b976` moves the `toast` imports in
`OrganizationSkillSharingDialog.tsx` and `SkillEnabledSwitch.tsx` to
`#/components/Toaster/toast`, as main's Biome import rule now requires,
and resolves conflicts with main in the model and MCP server sharing
dialogs.
28. `ac3e62c83b7` test: assert the member page's organization switch
through requests. After a switch, the test checks that the list and the
viewed skill are requested from the selected organization (a Codex
review finding).
29. `63bb42754e3` fix: show read-only skill status instead of a disabled
switch (from jaaydenh's review). `SkillEnabledSwitch` loses its
`disabled` prop; a new `ReadOnly` story in `SkillsTableView.stories.tsx`
shows the icons, and the read-only case in `SkillsTable.test.tsx`
asserted the status icon and no switch (removed again in item 30).
30. `82b2487ad17` test: address Codex review on skills tests. The
`SkillsPage.test.tsx` update-only case now edits the description, saves,
and asserts the `updateOrganizationSkill` call. The read-only status
case in `SkillsTable.test.tsx` is removed because the `ReadOnly` story
covers that visual state, and `renderTable` loses its `access` argument.
`AISettingsSidebar.test.tsx`'s hoisted permissions mock drops its cast
for an explicit return type.
31. `658be8b8df5` merges the updated sharing layer (main at
`e7b76872098`); no changes of its own.

## Size

Production +1465/-617, tests and stories +1104/-141 (tests +690 net,
stories +273 net), generated +4/-0; total +2573/-758. The site changes
in this PR and #30613 net +2263 lines, against +3058 for the same scope
in the old stack. This is above the 500-line reviewer-friendly limit on
purpose: Mike asked for 4 to 5 PRs instead of the 21 small ones this
stack replaces.

## Replaces

#30590 (site rename), #30595 (site API), #30596 (skills table), #30597
(Enabled toggle), #30598 (organization page), #30599 (ACL autocomplete),
#30600 (permissions), #30603 (member page).

## Validation

- `pnpm run lint` clean (tsc, oxlint, knip, React Compiler) and Biome
clean; `pnpm run lint` reran clean on `13f03d095ff`, which contains this
PR up to `0e55137de7b`.
- Vitest: 9 files, 49 tests. Storybook Vitest: 7 files, 101 tests. These
full runs were on `a52f9ba659c`; on `0e55137de7b`, the three test files
the later fixes change (`api/queries/skills.test.ts`,
`AISettingsSidebar.test.tsx`, `SkillEditor.test.tsx`) and the two
`SkillsPage` tests that cover the changed `SkillsPage.tsx` pass, 20
tests.
- On `a6f8f63ad13`, which contains this PR up to `5ebf7b8984a` and both
main merges: 38 Vitest files (the skill queries, the sidebar, the skill
pages and components, the slash menu, and `pages/AgentsPage/utils`)
pass, 542 tests with 2 skipped.
- On `63bb42754e3`: `pnpm check` and the full `pnpm run lint` pass;
Vitest (`SkillsTable`, `SkillsPage`, `AgentSettingsSkillsPage`: 6 tests)
and Storybook Vitest (the `SkillsTableView`, `AgentSettingsSkillsPage`
and `SkillsPage` stories: 34 tests) pass; `coder-frontend-review`
passed.
- On `82b2487ad17`: `pnpm check`, the full `pnpm run lint`, and Vitest
on the three changed test files (7 tests) pass.
- On `658be8b8df5`: `go build`, `pnpm check` and the full `pnpm lint`
pass; 6 Vitest files with 39 tests pass.
- `frontend-review` (FE1 to FE10) and `coder-frontend-review` (8
reviewer roles); findings fixed or declined as noted above.
- Remote UAT round 7 through Coder Agents passed on the whole stack,
last on `13f03d095ff`, which contains this PR up to `0e55137de7b`; see
#30613's Validation for scope and findings.
- Remote UAT round 8 passed on `b03c14c86de` (one item graded on intent;
see #30613), which contains this PR up to `9c802557b9c`: the member page
organization picker, list refresh after writes on the personal and admin
tables, no stale ACL after deleting and recreating a skill, and the
sidebar Skills `?org=`. See #30613 for details. `9002dc60382`,
`ac3e62c83b7`, `63bb42754e3`, `82b2487ad17` and the main merges have
tests but no UAT run.

> Xum opened this PR on behalf of @ibetitsmike.
@ibetitsmike
Merge main into chats
557bd29
@ibetitsmike
ibetitsmike merged commit adcce66 into main Oct 13, 2026
32 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@ibetitsmike
ibetitsmike deleted the mike/skills/chats branch October 13, 2026 17:13
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@chatgpt-codex-connector chatgpt-codex-connector[bot] chatgpt-codex-connector[bot] left review comments
@ThomasK33 ThomasK33 ThomasK33 approved these changes
@nickvigilante nickvigilante Awaiting requested review from nickvigilante

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@ibetitsmike ibetitsmike

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@ibetitsmike @ThomasK33
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.