Skip to content

feat: layered profile/bundle model and profile-first init UX#490

Closed
HarryCampion wants to merge 25 commits into
mainfrom
feat/profile-resolver
Closed

feat: layered profile/bundle model and profile-first init UX#490
HarryCampion wants to merge 25 commits into
mainfrom
feat/profile-resolver

Conversation

@HarryCampion
Copy link
Copy Markdown
Collaborator

Summary

Implements the rhiza-cli side of the two-layer bundle/profile model. Depends on Jebel-Quant/rhiza#1028 being merged.

  • ProfileDefinition + RhizaBundles.profiles — parse and round-trip the profiles: section of template-bundles.yml
  • RhizaTemplate.profiles — new profiles: list[str] field; old template.yml files without it load unchanged
  • resolve_bundles() — expands profile names → bundle lists, merges explicit templates:, deduplicates in order, raises ValueError for unknowns
  • sync — threads resolve_bundles through _clone_template; lock records resolved effective bundle list; accepts profiles: as valid config
  • validate — accepts profiles: as a valid configuration mode; new _validate_profiles() check
  • init — profile-first UX: fetches upstream profiles at init time, presents numbered menu, writes profiles: to template.yml; falls back to legacy templates: list when offline

Tests

26 new unit tests in tests/test_models/test_profile_resolver.py covering:

  • ProfileDefinition and RhizaBundles.profiles parsing/round-trip/errors
  • RhizaTemplate.profiles field serialisation
  • resolve_bundles — expansion, deduplication, multi-profile, all error paths

179 existing tests pass unchanged (4 updated for the _clone_template 4-tuple return and new error message wording).

Tied to

Jebel-Quant/rhiza#1028 — bundle/profile schema in upstream template-bundles.yml

Implements the rhiza-cli side of the bundle/profile two-layer model
introduced in rhiza#1028 (feat/bundle-profiles).

Changes:
- models/bundle.py: add ProfileDefinition dataclass; add profiles field
  to RhizaBundles with full from_config/config round-trip support
- models/template.py: add profiles: list[str] field to RhizaTemplate
  (backward compatible — old files without profiles: load unchanged)
- models/_profile_resolver.py: new resolve_bundles() function that
  expands profile names to bundle lists, merges explicit templates:,
  deduplicates preserving order, and raises ValueError for unknowns
- models/__init__.py: export ProfileDefinition and resolve_bundles
- commands/sync.py: thread resolve_bundles through _clone_template so
  profiles: in template.yml drive sparse checkout; lock records resolved
  effective bundle list; accepts profiles: as a valid config mode
- commands/validate.py: accept profiles: as valid configuration mode;
  add _validate_profiles(); update error messages
- commands/init.py: profile-first UX — fetch upstream profiles at init
  time, present numbered menu, write profiles: to template.yml; falls
  back to legacy templates: list when offline or no profiles in upstream
- tests/test_models/test_profile_resolver.py: 26 new unit tests
- tests/test_models/test_template_model.py: update for 4-tuple return
  from _clone_template and updated error message strings

Tied to: Jebel-Quant/rhiza#1028
@HarryCampion HarryCampion requested a review from tschm May 2, 2026 19:27
…prompts

- Add _RHIZA_STYLE (cyan #00BCD4) to all questionary calls in init.py
- Replace typer.echo + typer.prompt repo selection with questionary.select
- Replace raw input() in uninstall.py with questionary.confirm + _RHIZA_STYLE
- Update uninstall tests to mock questionary.confirm instead of builtins.input
- Remove now-unused typer import from init.py
Move the green colour to the 'checkbox' token so only the ●/○ marker
is green. Reset 'selected' to dim grey so checked row text matches
unchecked text. Set 'highlighted' to bold-only to prevent the terminal
reverse-video block on hover.
Remove all row text styling — both checked and unchecked items use the
same dim grey. The filled/hollow shape of the cyan ●/○ marker is the
sole selection indicator. Pointer moves without any row highlighting.
…text highlighting

- Set 'selected' to cyan so ● is filled+cyan when checked
- Set 'highlighted' to noreverse to suppress terminal reverse-video on hover
- Pass title as raw styled tuple list so questionary bypasses class:selected
  and class:highlighted for text entirely — row text is always dim grey
  regardless of checked or hover state
- Pointer movement is the only positional indicator
Patch questionary list layouts so the terminal cursor is hidden for
interactive list windows. This removes the square block drawn around
the active bundle bullet, leaving the cyan pointer and filled vs hollow
bullet markers as the only selection cues.

Adds a regression test covering the patched list window behavior.
Questionary was emitting a SetCursorPosition token for the active row,
which caused some terminals to draw a square cursor on the bundle
bullet. Strip that token from list rows so the only changing controls
are the cyan pointer and the hollow/filled bullet markers.

Adds a regression test for the patched choice tokens and keeps the
prompt-profile tests green.
Copy link
Copy Markdown
Member

@tschm tschm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not green?

@HarryCampion
Copy link
Copy Markdown
Collaborator Author

not green?

I'll come back to this

@tschm
Copy link
Copy Markdown
Member

tschm commented May 30, 2026

@HarryCampion let's discuss this. I think the functionality you are aiming for is now included. Maybe a candidate for closure?

@HarryCampion
Copy link
Copy Markdown
Collaborator Author

@HarryCampion let's discuss this. I think the functionality you are aiming for is now included. Maybe a candidate for closure?

Looks like it, I'll give it a go and close this.

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.

2 participants