Skip to content

feat(cli): clearer PiecesOS readiness wording#453

Merged
gavin-at-pieces merged 2 commits into
mainfrom
feat/clarify-pieces-os-readiness-wording
May 28, 2026
Merged

feat(cli): clearer PiecesOS readiness wording#453
gavin-at-pieces merged 2 commits into
mainfrom
feat/clarify-pieces-os-readiness-wording

Conversation

@gavin-at-pieces

Copy link
Copy Markdown
Contributor

Summary

  • Replaces vague PiecesOS readiness wording in open/onboarding/settings surfaces.
  • Adds top-level pieces.readiness_messages helper to avoid pieces.core circular import risk.
  • Adds tests for wording, no .port.txt claims, no install-state overclaim, and subprocess import smoke.

Non-goals

  • No doctor command.
  • No MCP changes.
  • No vendored SDK changes.
  • No install_pieces_os.py changes.
  • No behavior changes to PiecesInstaller, open_pieces_os, or Settings.startup.

Why a top-level helper

Every module under pieces.core imports Settings at module top, so pieces.settings -> pieces.core.<x> would re-enter a partially-loaded pieces.settings and raise ImportError. The helper lives at top-level src/pieces/readiness_messages.py with stdlib-only imports, mirroring how pieces.urls is safely imported by pieces.settings. A subprocess import-smoke test in tests/test_readiness_messages.py guards against regressions.

Wording

Long-form (used by open_command.py and the onboarding headline; rendered via Settings.logger.print(Markdown(...))):

PiecesOS is not reachable from the CLI.

Possible reasons:

  • PiecesOS may need to be installed or updated
  • PiecesOS may not be running
  • PiecesOS may still be starting up

Try:

  1. Install or update PiecesOS: `pieces install`
  2. Launch PiecesOS: `pieces open`
  3. If you just installed it, wait a few seconds and retry your command
  4. If the problem persists, share the output of `pieces version` with Pieces support

Short-form (used by the settings.py Rich Progress description; must stay single-line):

[red]Could not reach PiecesOS - try `pieces install`, then `pieces open`

Files

Edited (3):

  • `src/pieces/core/open_command.py` — replaces `"PiecesOS is not running"`
  • `src/pieces/core/onboarding.py` — replaces only the `"❌ PiecesOS is not running"` headline (marketing panel and `pieces install` OnboardingCommandStep untouched)
  • `src/pieces/settings.py` — replaces only the single-line `progress.update(..., description=...)` string in `open_pieces_widget()`

Added (2):

  • `src/pieces/readiness_messages.py` — top-level, stdlib-only
  • `tests/test_readiness_messages.py` — 18 tests (helper wording, short-form shape, subprocess import-smoke, 6 parametrized regression cases asserting the three edited call sites do not reintroduce the legacy overclaiming strings)

Validation

  • Import smoke for `pieces.settings`, `pieces.core.open_command`, `pieces.core.onboarding`, `pieces.readiness_messages` -> ok
  • `pytest tests/test_readiness_messages.py -v` -> 18 passed
  • `pytest tests/test_user_friendly_errors.py -v` -> 19 passed
  • `pytest tests/mcps/mcp_gateway/test_validation_core.py tests/mcps/mcp_gateway/test_validation_advanced.py -v` -> 30 passed
  • `pytest tests/ -q --ignore=tests/mcps/mcp_gateway/test_e2e.py --ignore=tests/links_test.py` -> 360 passed, 24 skipped
  • `ruff check src/pieces/readiness_messages.py src/pieces/core/open_command.py src/pieces/core/onboarding.py src/pieces/settings.py tests/test_readiness_messages.py` -> all checks passed

Why `test_e2e.py` and `links_test.py` are excluded

Both files reproduce as pre-existing environmental failures on clean main, not caused by this PR. Verified by re-running each on `main` with this branch's changes stashed:

  • `tests/mcps/mcp_gateway/test_e2e.py` — 3 failures, all with the literal message `"Gateway timed out - this should not happen with POS running"`. These require a live local PiecesOS reachable inside the 10-15s test timeout to handle a real JSON-RPC handshake; fails on any box where PiecesOS isn't running, regardless of this PR.
  • `tests/links_test.py` — fails on `RAYCAST_MCP_DOCS` (`https://manual.raycast.com/model-context-protocol\` currently returns HTTP 404). External URL drift, unrelated to this PR.

Including them would mask the real signal from this PR's targeted suites, so they are skipped only for the full-suite run. The targeted MCP gateway validation suites (`test_validation_core.py`, `test_validation_advanced.py`) are run separately and pass.

Manual smoke

Forced the open_command failure branch via the `copilot` kwarg path (the one explicitly called out for manual smoke) by mocking `open_pieces_os` to return False, since PiecesOS is currently running on the dev box. Output is the rendered long-form Markdown block with bullets and numbered next steps; short-form renders as a single-line red Rich progress description.

Test plan

  • CI green on this branch
  • Reviewer eyeballs the long-form and short-form messages in a real failure scenario (PiecesOS stopped) by running `pieces open --copilot`, `pieces onboarding`, and a Settings.startup-touching command such as `pieces list`
  • Confirm the marketing Markdown panel in onboarding (L271-287) and the `pieces install` OnboardingCommandStep (L289-291) still render unchanged

@gavin-at-pieces gavin-at-pieces force-pushed the feat/clarify-pieces-os-readiness-wording branch from 384e9e9 to b6970c2 Compare May 28, 2026 20:14
@gavin-at-pieces gavin-at-pieces marked this pull request as ready for review May 28, 2026 20:21

@mark-at-pieces mark-at-pieces left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review: Approve

Clean, focused PR that solves a real UX problem (misleading error messages) with appropriate defensive architecture and strong testing. Ship it.


Correctness

The old messages ("PiecesOS is not installed", "PiecesOS is not running") overclaimed what open_pieces_os() can actually determine — it only knows the service is unreachable, not why. The new "not reachable" framing is factually accurate and gives users actionable next steps without false certainty.

All three edited files only replace string arguments in existing print/progress calls. No control flow changes, no new branches, no changed return values.

Architecture

Placing the helper at src/pieces/readiness_messages.py (top-level, stdlib-only) is the correct call. Every pieces.core module imports Settings at module top, so placing this under pieces.core would create a pieces.settings → pieces.core.readiness_messages → pieces.settings cycle. The chosen location mirrors the existing pieces.urls pattern — proven safe in this codebase. The module docstring clearly documents these constraints for future maintainers.

Testing

18 tests is thorough for a wording change:

  • Subprocess circular-import smoke test — particularly well-designed. Running in a subprocess ensures Python's module cache can't mask a real cycle (in-process tests would pass even with a cycle because modules are already cached).
  • Parametrized regression guards (3 files × 2 forbidden strings) prevent future drift back to overclaiming language.
  • Excluded tests (test_e2e.py, links_test.py) are justified as pre-existing environmental failures on clean main.

Two minor suggestions (non-blocking)

  1. Self-referential suggestion in open_command.py: When a user runs pieces open --copilot and PiecesOS fails to launch, the readiness message suggests "Launch PiecesOS: pieces open" as step 2 — which is what just failed. In practice this is probably fine since plain pieces open (without flags) follows a different code path, and the generic message serves all call sites well. Just noting the mild awkwardness.

  2. Unclosed Rich tag in pieces_os_not_reachable_short(): The string starts with [red] but never closes with [/red]. Rich auto-applies unclosed tags to the rest of the string (works correctly here), but explicit closure would prevent accidental bleed if the string is ever concatenated with other content.

Performance / Security / Dependencies / Breaking Changes

No concerns across any of these dimensions. Stdlib-only module, no new deps, no API changes, no input handling affected.


Nice work on the defensive module docstring and the subprocess import test — both demonstrate good engineering instincts around preventing regression in a codebase with known import-order sensitivity.

mark-at-pieces
mark-at-pieces previously approved these changes May 28, 2026
@gavin-at-pieces gavin-at-pieces merged commit f959c39 into main May 28, 2026
7 checks passed
@gavin-at-pieces gavin-at-pieces deleted the feat/clarify-pieces-os-readiness-wording branch May 28, 2026 22:16
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