loadSkillCommands() published readiness in a finally block that ran even when
/api/skills rejected, so one transient failure left
_skillCommandCacheReady=true with an empty cache. Because
ensureSkillCommandsLoadedForAutocomplete() only re-loads while
!_skillCommandCacheReady && !_skillCommandLoadPromise, no later picker pass ever
retried: skill commands stayed missing until a page reload even after the API
recovered.
Readiness is now set only after a successful current-generation commit; the
promise is cleared either way so the next pass can retry. The stale-generation
fence is unchanged (verified a reply from the outgoing profile still refuses to
commit).
This wedge is PRE-EXISTING on master (its finally sets ready unconditionally),
not introduced by this PR — fixed here because this change already reworks the
surrounding generation handling. Adds a reject-once/recover regression plus a
__failSkillsOnce() harness knob; reverting the fix turns it red.
Co-authored-by: webtecnica <webtecnica@users.noreply.github.com>
The slash-skill caches (_slashSkillCache, _slashSkillCachePromise,
_skillCommandCache) outlived a profile switch, so the outgoing profile's
/api/skills payload kept hiding skills that the incoming profile enables
(#7509).
- call invalidateSlashSkillCaches() from both switch handlers
(static/panels.js switchToProfile, static/sessions.js
_switchProfileForSessionLoad), after the /api/profile/switch request
- bump a generation counter in invalidateSlashSkillCaches() and capture it
in both loaders: a reply issued for the outgoing profile can no longer
commit once the switch has landed
- tests: in-flight race and cache-refresh regression on both picker
surfaces, plus a check that both switch paths call the invalidation
`skills.disabled` entries are already excluded from the backend skill-command
map, but the composer still offered them as selectable slash commands and as
`/use` sub-args: both picker surfaces read `/api/skills` and ignored the
`disabled` flag the endpoint reports per skill.
Add one `_isSkillDisabled()` gate in static/commands.js and apply it at the two
places that turn `/api/skills` rows into completions:
* `_buildSkillCommandEntry()` returns null for a disabled skill, which keeps
`_skillCommandCache` (and every consumer of it, including the dropdown) clean;
* `_loadSlashSkillSubArgs()` skips disabled skills when building `/use`
sub-args.
Both caches still rebuild from `/api/skills`, so a skill toggled off in the
skills panel disappears from the picker on the next load.
Adds tests/test_issue7509_disabled_skills_slash_picker.py, which runs the real
static/commands.js in a vm context with a mocked `api()` and asserts on
`getSlashAutocompleteMatches()` for both surfaces, over 0 / 1 / many skills,
enabled / disabled / flag-absent entries and a disable-then-refresh cycle.
Fixes#7509