Global skills
Platform-global (and partner-global) skills reach every eligible scope's resolve response without any per-scope binding. Use for cross-cutting capabilities that every agent should have — a document-authoring skill, a support playbook, a compliance guide.
Shipped 2026-07-03. Three layers: v1 platform-global, v2 partner-global, v3 per-scope opt-out.
v1 — platform-global#
A super_admin marks a skill global and pins the served version. From that moment forward every resolve response — for every tenant, every channel, every user — includes the skill.
1 2 3 4 5 6 7 | |
Response:
1 2 3 4 5 6 7 8 9 10 11 | |
Version pinning#
Global skills always serve the pinned version — never latest. This is deliberate: a bad release can't silently propagate to every tenant.
- Yanking the pinned version un-globals the skill until a super_admin re-pins.
- Deprecating the skill has the same effect.
- Only one non-yanked published version per skill can be pinned at a time (enforced in a single transaction).
To move to a new version, call PUT .../global again with the new version — the old pin is cleared and the new one set atomically.
Clearing#
1 2 3 4 5 6 7 8 9 | |
Body-strict schema#
The request body uses additionalProperties: false. A misdirected field returns 422 with the offending field named — deliberately prevents the "silently-ignored path" class of bug:
1 2 3 4 5 | |
v2 — partner-global#
Same route, scope_type: "partner" + scope_id: "<partner_id>". Reaches only tenants under the specified partner.
1 2 3 4 5 6 7 8 9 | |
Authority:
super_admin— can set platform or partner scope for any partner.partner_admin— can set partner scope for their OWN partner (scope_idmust equal the caller'spartner_id).- Any other role — 403.
Response includes the scope:
1 2 3 4 5 6 7 8 9 10 11 | |
v3 — per-scope opt-out#
Tenants (or users, or channels) that don't want a specific global can opt out. The global stays live for everyone else; the opting-out scope just stops seeing it.
1 2 3 4 5 6 7 8 | |
Authority:
super_admin— unrestricted.tenant_admin/partner_admin— can opt out for their OWN workspace.- Any user — can opt out for their own
userscope. channel/corescope opt-outs require thescaiskills:managemodule permission.
Idempotent. Hitting the same (slug, scope) twice returns the same row.
Reversing:
1 2 3 | |
Returns {"removed": false} (still 200) if there was no opt-out to remove — safe to run in idempotent playbooks.
Opt-outs affect both /resolve AND /mcp/skills/view — a scope can't hide a global from resolve and still fetch its SKILL.md via view. Same guard.
Resolve response — the source field#
Every resolved skill now carries a source field so consumers can badge globals in the preamble:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
Consumers may render "🌐 Global" badges next to source: "global" entries so agent authors know the skill isn't per-tenant configured.
Precedence#
If a scope has BOTH a global and a binding for the same slug, the binding wins. Full order (highest wins):
core > user > channel > workspace > global
This lets a tenant "pin" a different version of a global skill — bind their preferred version to scope_type: "workspace" and it overrides the global for their tenant only.
Cache behavior#
Global-flag changes affect every scope's resolve result. Instead of scanning and invalidating every cache key, the resolver keys its Redis cache under a versioned prefix that bumps on any set_global / clear_global / opt_out / opt_in:
scaiskills:resolve:v{N}:{scope_type}:{scope_id}:p{partner_id_or_-}
Old keys (v{N-1}...) expire naturally at their 60 s TTL. Effect: a super_admin's PUT .../global shows up in every tenant's next resolve within 60 seconds worst case, immediately for scopes that resolve before their old cache expires.
Catalog fields#
GET /v1/modules/scaiskills/skills and GET /skills/{slug} now carry three new fields per row so admin UIs can render "Make global" / "Global" state:
is_global(bool)global_version(semver ornull)global_scope_type("platform"|"partner"|null)
Audit#
Every set / clear / opt-out / opt-in writes an audit row with action set to one of:
scaiskills.global.setscaiskills.global.clearscaiskills.global.opt_outscaiskills.global.opt_in
Filter the audit log with ?action=scaiskills.global.set to see everything that's been made global platform-wide.