Skip to content

Old custom skills no longer break the upgrade: Core boots, incompatible skills are cleaned up, missing ones are named - #420

Merged
Shackless merged 6 commits into
developfrom
fix/legacy-skills-and-211-migration
Sep 20, 2026
Merged

Shackless merged 6 commits into
developfrom
fix/legacy-skills-and-211-migration

Conversation

@Shackless

@Shackless Shackless commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Found while migrating a real user's 2.1.1 configs (Xul: 9 configs, 18 Wingmen, 479 commands, three custom skills) to 3.2.3 and then booting Core with his actual sc_log_reader v4.9.0.0 installed.

Core did not start at all

One Wingman carried a skill setting the installed skill version no longer defines:

Invalid config in 'Angel.yaml':
Config validation failed for wingman 'Angel' — 2 issues:
  • wingman['Angel'].skills['Star Citizen Log Reader'].custom_properties['debug_file_output'].name: required field 'name' is missing
  • ...property_type: required field 'property_type' is missing
INFO:     Shutting down

A Wingman stores only {id, value} per skill property; name and property_type come from the skill's default_config.yaml. When a skill update drops a property, nothing can complete the stored entry, the merged Wingman fails validation, parse_config re-raises and the process ends. One stale key in one of 18 files is enough. This has nothing to do with the migration — it hits anyone who updates a custom skill that lost an option.

ConfigManager._drop_orphaned_skill_properties now drops such a value while building the merged config. The YAML on disk is untouched, and an entry that carries the whole property itself still survives, so skills that ship their metadata inside the Wingman config keep working.

Incompatible skills were only cleaned in the config that happened to be open

disable_ineligible_skills walked current_config_dir only. With nine configs, SC_LogReader stayed switched on in the other eight until the user opened them — and the switch did nothing. It now sweeps every config, and the skill's stored settings go with it: a skill that has to be rewritten for the v3 API gets new properties anyway.

Removed incompatible skill(s) ['SC_LogReader'] from wingman 'VO/Violet', including their settings.
The skill needs an update from its developer and has to be installed again.

Skills that are not installed at all were invisible

SC_Accountant and SC_Navigator are switched on for two Wingmen but their folders are not on this machine. Core cannot describe them, so they never appeared in /wingman-skills — no row, no badge, no hint. New GET /wingman-skills/missing returns them by name; the client renders a disabled row (ShipBit/wingman-client#51). These are deliberately not removed: the folder may simply live on another machine.

Migration

  • migration_211_to_300 pinned parakeet.execution_provider to cpu. hardware_scan_performed is already true for every 2.x user (2.0.0 set it for FasterWhisper), so the boot-time scan never runs again and every GPU owner would transcribe on the CPU forever. It now asks the machine, the same way 1.8.2 → 2.0.0 did for FasterWhisper, and logs the result.
  • The .migration marker was written at the end of the last chain step, so the custom-skill warnings and the closing line only ever reached the terminal — not the file users send us. The full log is flushed at the end now.
  • New audit_skill_availability() names every skill a migrated Wingman references but that is not installed, for both discoverable_skills (names, what the UI shows as on) and skills (module paths, the settings).
  • Migrating Wingman … and Copied file: … lines carry the config dir now. Xul has zRandom NPC three times; a bare file name made the log unreadable for exactly the people with the most to lose.

Verified

Migration re-run from scratch four times against the untouched 2.1.1 folder (checksums over all 150 files unchanged): 9 configs, 18 Wingmen, 479 commands, backstories, avatars and PTT keys identical, one default config instead of two, no duplicates. Boot with the real legacy skill installed now reaches ready and cleans up as described. 319 local tests pass, including two new files for the GPU detection and the orphaned properties (/tests/ is gitignored by house convention, so they stay local).

Skill verdicts can be asked for now

legacy_v2, quarantined and failed only existed as a WebSocket broadcast during initialize_tower. Reloading the client while Core keeps running lost the badges, and an incompatible skill sat there switched off with nothing saying why. GET /skill-verdicts returns the same records as a snapshot — scan verdicts with this boot's runtime failures folded in — and the client fetches them with the skill list.

Core no longer dies on the joystick thread (macOS)

start_joysticks ran pygame.init(), which starts every pygame module: the mixer, which opens an output device while Core's audio stack already holds one, and the video subsystem, which on macOS reaches into Cocoa and sets the application's main menu — only allowed on the main thread:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'API misuse: setting the main menu on a non-main thread.'

Any Wingman with a joystick PTT binding killed Core on macOS. The loop only reads joystick events and never draws, so it now initializes display and joystick only, with the dummy video driver on macOS. pygame.event.get() refuses to work without a video driver, which is why display is initialized but left without a window. Windows and Linux keep their driver; the joystick path there needs a check on Windows, I could not test it.

Verified on macOS with the Wingman that used to kill Core (RIGHT VPC Stick WarBRD button 28): the tower initializes fully and Core stays up.

The system prompt is reset again in 3.2.3

The same hard reset 3.1.2 did. It is not user content: it carries the tool-calling contract, the output format and the placeholders the backend fills in, and every release moves it. Someone who edited it once keeps an old contract forever and gets worse answers from a backend that has moved on.

  • defaults.yaml is overwritten with the prompt shipped in this build.
  • A Wingman's own prompts.system_prompt is deleted, not overwritten, so it inherits from defaults.yaml again. Overwriting would pin today's text into the file and the next release would have to chase it a second time.
  • Backstories are untouched. That one IS user content, and it stays.

Verified end to end, not only in unit tests: a 3.2.2 install was planted with an edited defaults.yaml prompt (# MY OWN RULES / Ignore every tool) and a Wingman carrying its own (You are a pirate. Never call a function.), then migrated for real. The log:

Migrating from 3.2.2 to 3.2.3
- defaults: system prompt reset to the shipped one
- Computer: removed its own system prompt; it uses the shipped one now

Afterwards defaults.yaml matches templates/configs/defaults.yaml byte for byte, none of the 18 Wingmen carries a system_prompt, and all 18 backstories are still there.

@Shackless
Shackless merged commit b07f31c into develop Sep 20, 2026
4 checks passed
@Shackless
Shackless deleted the fix/legacy-skills-and-211-migration branch September 20, 2026 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant