Skip to content

Favorite skills: robot-persisted per-user curation of the Skills menu - #556

Closed
theo-michel wants to merge 2 commits into
mainfrom
feat/curated-skills-menu
Closed

Favorite skills: robot-persisted per-user curation of the Skills menu#556
theo-michel wants to merge 2 commits into
mainfrom
feat/curated-skills-menu

Conversation

@theo-michel

@theo-michel theo-michel commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

What

Users star skills; the Skills menu shows favorites by default (or the whole roster until the first star), with the rest behind "Show all (N more)". Favorites live on the robot — not in any client — so the webapp and the mobile app share one list:

  • /brain/favorite_skills — latched + heartbeated broadcast, String JSON {"skills": [ids]}
  • /brain/set_favorite_skills — full-list replace from any client (star toggle publishes the whole updated list; the broadcast echo confirms)
  • Persisted in workspace/.preferences/favorite_skills.json (gitignored), atomic writes, survives restarts and corrupt files

Also fixes row labels showing roster paths: innate-os/pick_socks now renders as "Pick Socks" (source prefixes are plumbing, not UI).

History / design

The first commit curated via a hardcoded list in webapp JS; that was the wrong layer — curation must be robot-side so all clients agree — and shipped defaults shouldn't override user judgment. This supersedes it with user-owned favorites. The principle stands: exposure is a consumer/user list, never author-side metadata on skills (RFC included: docs/SKILLS_AS_FUNCTIONS_RFC.md, draft for discussion, not implemented here).

Assumption: clients are unauthenticated, so "per user" = per robot (one favorites list per machine). If we later grow user identities, the store keys naturally by user file.

Mobile app: adopts the same two topics via rosbridge — no innate-os changes needed when it does.

UX details

  • First star ever auto-flips to "Show all" so the list doesn't collapse to one row mid-browse
  • Star sits beside the run button (never nested — a mis-tap must not launch the skill)
  • Re-renders are membership-gated (latched heartbeats don't steal form focus)
  • A favorite whose skill is mid-reload survives (store keeps unknown ids; the view just doesn't show them)

Testing

  • pytest ros2_ws/src/brain/brain_client/test/test_favorite_skills.py — 7 tests (sanitize, persistence round-trip, corrupt-file recovery, legacy bare-array format); added to the CI fast bucket in ci/run_integration_tests.sh
  • node webapp/tests/skillsMenu.test.js — 6 tests on the pure helpers (favorites filter, first-run behavior, path-hiding labels)
  • Browser end-to-end against the real skillsMenu.js with a stubbed rosClient: first-run shows all with outline stars → first star keeps the list expanded and publishes {"skills":[...]} → "Show fewer" shows exactly the starred rows → a robot-side list change (as the mobile app would make) updates the view → unstarring all returns to first-run
  • ruff + pre-commit (CI config) pass on all touched Python

The Skills menu showed every skill on /brain/available_skills — building
blocks, chess/email app skills, demos — burying what an operator actually
launches. Rather than author-side visibility metadata (hidden flags,
folders, tags), exposure now belongs to the consumer: the menu shows

  - every user skill (local/),
  - the shipped DEFAULT_COCKPIT_SKILLS curated list,
  - the active directive's skills from /brain/agent_status while the
    brain is active,

with a "Show all (N more)" footer keeping the rest reachable. Zero
server changes — agent_status already carries active_skills (latched +
heartbeat). Management surfaces (Datasets, agent skill toggles) still
see the full roster.

The rule lives in skillVisibility.js (pure) with plain-node tests,
including a filesystem check that keeps DEFAULT_COCKPIT_SKILLS honest
against workspace/innate_skills/.

Also adds docs/SKILLS_AS_FUNCTIONS_RFC.md — the follow-up direction
(@Skill decorator over plain functions; skill_lib narrows to pure
computation) for team discussion.
@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a curated, robot-persisted favorites view to the cockpit Skills menu. The main changes are:

  • A ROS publisher and subscriber for shared favorite skills.
  • Atomic on-robot persistence for the favorites list.
  • Favorite controls and a full-roster toggle in the cockpit menu.
  • Unit tests, styles, CI coverage, and a skills-as-functions RFC.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
ros2_ws/src/brain/brain_client/brain_client/nodes/skills_server.py Adds publication, replacement updates, persistence wiring, and heartbeat delivery for favorite skills.
ros2_ws/src/brain/brain_client/brain_client/skills/favorites.py Adds sanitization and atomic file persistence for the ordered favorites list.
webapp/js/teleop/skillsMenu.js Adds favorite-based menu curation, star controls, and a full-roster toggle.
webapp/tests/skillsMenu.test.js Tests favorite filtering, roster ordering, and operator-facing labels.
ros2_ws/src/brain/brain_client/test/test_favorite_skills.py Tests persistence, sanitization, corrupt input, unknown identifiers, and legacy data.

Reviews (2): Last reviewed commit: "feat(skills): robot-persisted favorite s..." | Re-trigger Greptile

Replaces the hardcoded DEFAULT_COCKPIT_SKILLS JS list from the previous
commit: curation must live on the robot, not in one client, because the
mobile app needs the same view. The user stars skills; the list persists
in workspace/.preferences/favorite_skills.json (gitignored) and is shared
by every client:

  /brain/favorite_skills      latched + heartbeated {"skills": [ids]}
  /brain/set_favorite_skills  full-list replace from any client

The webapp menu shows favorites by default (or the whole roster until
the first star — an empty menu teaches nothing), stars each row, and
keeps the rest behind "Show all". The first star flips to Show-all so
the list doesn't collapse mid-browse. Row labels no longer show the
roster path prefix (innate-os/wave -> "Wave").

FavoriteSkillsStore is ROS-free (stdlib only) with pytest coverage in
the CI fast bucket; the menu's pure helpers (favoriteSkills, prettify,
formatName) are exported and covered by webapp/tests/skillsMenu.test.js.
The agent_status-driven union from the previous commit is dropped —
favorites are the one curation concept.
@theo-michel theo-michel changed the title Curated cockpit Skills menu (consumer-side lists) + skills-as-functions RFC Favorite skills: robot-persisted per-user curation of the Skills menu Jul 21, 2026
@theo-michel

Copy link
Copy Markdown
Contributor Author

Superseded — the favorites work (and the skill_lib cleanup) moved into #542 per review, so the whole skills story lands as one PR. Branch left in place.

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