Skip to content

[Feature] Skillset toggle: "Always keep skills in this skillset up to date" (auto-track latest member versions) #1191

Description

@chronoai-shining

Background

A skillset references its members as name@ref strings, where ref is either pinned (foo@2.0, resolved literally) or moving (foo@latest → skill.latestVersion; foo@<dist-tag> → skill.distTags[tag]). The propagation engine already exists: when a member skill publishes a new version, fireSkillsetMirrorForMember → bumpRevisionsForChangedMember → maybeBumpRevisionForMemberChange recomputes the skillset's public-resolved member snapshot, and only if that snapshot moved does it cut a new skillset revision and re-export the plugin (syncSkillsetsForMember).

Consequence today: a member new version propagates to the skillset only if that member was set to a moving ref (@latest/dist-tag). A skillset whose members are pinned never auto-updates — the owner must re-point each member by hand. This is the friction reported in practice: chronoai-service-manual-bundle stayed at revision 1.2 after a member (ornn-agent-manual-cli) advanced to 1.5, because the member ref did not float.

Purpose

Add a single skillset-level opt-in toggle — display name "Always keep skills in this skillset up to date" — that makes the resolver treat every member as "resolve to latest," so any member new version auto-updates the skillset and (if exported) re-publishes the marketplace plugin, with no per-member ref juggling.

This is a lifecycle/registry convenience (the npm-analog: a set that always tracks each dependency's latest), exposed API-first on the skillset resource so agents can set it, with the ornn-web toggle as the secondary surface. It mirrors the existing exportAsPlugin opt-in in shape and default.

Decided semantics (product call): when the toggle is ON it overrides ALL member refs — pinned included — to each member's latest version. Pinning is intentionally inert while the toggle is on (simplest mental model; "always keep ALL up to date" is literal). The override is non-destructive: stored members refs are preserved and only overridden at resolution time, so turning the toggle off restores the original pinned/moving behavior.

Implementation Specification

New persisted field: autoUpdateMembers?: boolean on the skillset, default false. Suggested commit decomposition (each self-contained, tree green at every commit):

  1. feat(api): add autoUpdateMembers field to skillset schema + persistence

    • types.ts: add autoUpdateMembers?: boolean to SkillsetDocument (next to exportAsPlugin, ~L284) and to the read/view types (SkillsetView ~L379 and any DTO returned by the API).
    • repository.ts: default autoUpdateMembers: data.autoUpdateMembers ?? false on create (~L237); set-on-update guard if (data.autoUpdateMembers !== undefined) setFields.autoUpdateMembers = ... (~L278); project on read mapping (~L523). No new index required.
  2. feat(api): resolver honors autoUpdateMembers — resolve every member to latest

    • In the member-resolution path used by recompute — recompute.ts computePublicResolvedMembers (~L108) and/or the loader crud/service.ts createVersionLoader (~L1712) / resolveVersionRef (~L110) — when the owning skillset's autoUpdateMembers === true, resolve each member name to skill.latestVersion regardless of the stored ref (ignore pins and dist-tags). Preserve members: string[] unchanged (override only at resolution). Keep the existing public-only filter and the SKILLSET_MIN_PUBLIC_EXPORT_MEMBERS floor intact. Thread the flag into the resolver (read from the skillset doc already loaded by the recompute path).
    • No change needed to maybeBumpRevisionForMemberChange itself: once the resolver honors the flag, a pinned member's new version does move the public-resolved snapshot, so the existing diff-and-bump logic fires naturally.
  3. feat(api): immediate catch-up on enabling autoUpdateMembers

    • service.ts: when autoUpdateMembers transitions false → true, trigger an immediate recompute → revision bump → re-export for that one skillset (reuse the existing per-skillset recompute/bump entry that bumpRevisionsForChangedMember uses), so members that are already behind are brought current at once rather than waiting for the next member publish. Re-export to the mirror only when exportAsPlugin is on.
  4. feat(api): accept + validate autoUpdateMembers on the skillset update route

    • routes.ts: add autoUpdateMembers: z.boolean().optional() to the skillset update/PATCH body schema and pass it through to the service. Author/admin-authorized only (same guard as other skillset mutations).
  5. feat(web): "Always keep skills up to date" toggle in the skillset editor

    • ornn-web/src/types/skillset.ts: mirror the field.
    • ornn-web/src/hooks/useSkillsets.ts: include it in the update mutation.
    • ornn-web/src/components/skillset/SkillsetForm.tsx (or a settings section / near the SkillsetPluginExportCard): a labeled toggle "Always keep skills in this skillset up to date" with helper text stating plainly that while on, all members — including pinned ones — always resolve to their latest version. Follow docs/DESIGN.md.
  6. docs: changeset for autoUpdateMembers — bun changeset (minor bump, both packages).

Affected Files

  • ornn-api/src/domains/skillsets/types.ts
  • ornn-api/src/domains/skillsets/repository.ts
  • ornn-api/src/domains/skillsets/service.ts
  • ornn-api/src/domains/skillsets/recompute.ts
  • ornn-api/src/domains/skills/crud/service.ts (createVersionLoader / resolveVersionRef)
  • ornn-api/src/domains/skillsets/routes.ts
  • ornn-web/src/types/skillset.ts
  • ornn-web/src/hooks/useSkillsets.ts
  • ornn-web/src/components/skillset/SkillsetForm.tsx (+ export-card area if that's where the toggle lands)
  • Colocated unit tests for each of the above + integration test under ornn-api/tests/
  • .changeset/*.md

Verification Checklist

  • Toggle OFF (default): pinned member stays pinned; a member new version does not move a pinned skillset (unchanged behavior).
  • Toggle ON: a pinned member (foo@2.0) resolves to foo's latestVersion in the public-resolved snapshot.
  • Toggle ON: a member publishing a new version auto-bumps the skillset revision even though the member is pinned, and re-exports the plugin when exportAsPlugin is on.
  • Enabling the toggle (false→true) immediately bumps + re-exports if any member was behind; no-op if all members already latest.
  • Override is non-destructive: turning the toggle back OFF restores the original pinned resolution.
  • Min-public-member floor + public-only filter still enforced under the flag.
  • PATCH route validates the boolean and rejects non-boolean; author/admin only.
  • Web toggle renders, persists, round-trips, and its helper text states the pin-override behavior.
  • Success and failure/edge paths covered by tests; overall coverage stays ≥ 80%.
  • bun run test, bun run lint, bun run typecheck green; changeset present.

Definition of Done

A skillset owner can turn on "Always keep skills in this skillset up to date" via the API or ornn-web. While on, every member — pinned or not — resolves to its latest version; any member's new version automatically bumps the skillset revision and, for exported skillsets, re-publishes the marketplace plugin, with no manual per-member ref changes. Turning it off restores the previous pinned behavior. Fully tested and documented via changeset.


Related to #1155 (skillset plugin export), #1162 (auto-incremented skillset revision), #1165 (revision-bump-on-member-change). Distinct axis from #1176/#1178 (GitHub source → skill drift auto-sync); this is member skill version → skillset propagation.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

apiAPI design & endpointsphase:4M4 - Platform Powertype:featureNew user-facing capability.webornn-web frontend SPA

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions