Skip to content

feat: OVOS-GUI-1 service conformance — SYSTEM_ gate, dual frame vocab, per-session namespace stacks - #117

Draft
JarbasAl wants to merge 5 commits into
devfrom
feat/gui1-service-conformance
Draft

feat: OVOS-GUI-1 service conformance — SYSTEM_ gate, dual frame vocab, per-session namespace stacks#117
JarbasAl wants to merge 5 commits into
devfrom
feat/gui1-service-conformance

Conversation

@JarbasAl

Copy link
Copy Markdown
Member

Brings ovos_gui into line with the service-side MUSTs of OVOS-GUI-1
(§8.3) without disturbing the legacy QML/WebSocket render path. Scoped
deliberately to the tractable, non-breaking pieces; see Deferred below for
the parts that belong to the larger adapter/render rework (#112).

This is additive on top of current origin/dev (legacy mycroft-gui protocol);
the default single-screen behaviour is preserved exactly.

What this implements

§3.2 / §4.2 / §8.3 — SYSTEM_ prefix gate

New ovos_gui/templates.py is the single source of truth for the closed
SYSTEM_* template vocabulary (§3.4). handle_show_page recognises a template
intent by the reserved SYSTEM_ prefix. A first page_names entry that does
not begin with SYSTEM_ is recognised as not a template of this
spec and is logged as the deployment-specific legacy custom-QML path (§4.2) —
it is never dispatched as a template. Custom-QML skill pages keep rendering.

§3.1 / §8.1 — frame vocabulary (additive dual-accept)

The service now accepts both the legacy CamelCase frame names
(SYSTEM_TextFrame, SYSTEM_Status, …) and the GUI-1 spec names
(SYSTEM_text, SYSTEM_status, …). A spec name is resolved to the legacy QML
resource the current render backends ship (SYSTEM_textSYSTEM_TextFrame),
so the producer (ovos-bus-client) can rename its emitted frames to the
spec vocabulary without breaking rendering — unblocking the rename deferred
in ovos-bus-client #238.

§4.3 / §5.1 / §8.3 — per-session namespace stacks

Display state is partitioned per session_id via a new GUISession, read from
context.session and defaulting to the reserved "default" id (OVOS-SESSION-1
§3.1). Each session owns an independent namespace stack so sessions no longer
collide. The legacy single-screen QML transport maps to the "default"
session; loaded_namespaces / active_namespaces / remove_namespace_timers
are now read/write proxies onto that default session, so bus.py
synchronize() and all existing handlers behave unchanged.

Tests

  • test/unittests/test_templates.py — closed vocab, prefix gate accept/reject,
    legacy⇄spec alias maps, render-resource resolution.
  • test/unittests/test_gui1_conformance.py — boots the real NamespaceManager
    on a FakeBus and drives gui.page.show / gui.value.set /
    gui.clear.namespace: SYSTEM_ gate, legacy/spec dual-accept, default-session
    fallback, and per-session isolation of show/value-set/clear.
  • Existing test_namespace.py updated for the threaded session argument.

111 tests passing locally (88 prior + 23 new). The legacy custom-QML path,
spec→legacy resource resolution, and default-session non-interference were also
verified end-to-end against a booted manager.

Deferred — needs the adapter/render rework (coordination, not in scope here)

  • True multi-session delivery. The legacy QML/WebSocket transport is
    inherently single-screen: clients do not declare a session_id on connect,
    and bus.py broadcasts to all clients. This PR makes the service state
    fully per-session and correct, but actually delivering a non-"default"
    session only to its clients requires the adapter model (per-session_id
    client routing, §5.1). That lives in the adapter/template rework (feat: land GUI adapter/template rework (session_id-only); fix B1-B9 (supersedes #100) #112), which
    replaces the WebSocket server with opm.gui_adapter fan-out and is blocked on
    an unpublished ovos-plugin-manager + adapter plugins. Non-default sessions
    here are isolated in state and observable via the query surface, but the
    legacy transport still renders the "default" session.
  • Adapter fan-out / headless no-op / state-query surface (§6). Also owned by
    feat: land GUI adapter/template rework (session_id-only); fix B1-B9 (supersedes #100) #112.
  • Producer rename. Emitting spec frame names is a producer change in
    ovos-bus-client; this PR makes the service accept them so that rename can
    land safely.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@JarbasAl, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 33d4e459-d993-404d-be5c-53acb08634af

📥 Commits

Reviewing files that changed from the base of the PR and between 0be08cb and 38637dc.

📒 Files selected for processing (9)
  • .github/workflows/ovoscope.yml
  • ovos_gui/namespace.py
  • ovos_gui/templates.py
  • pyproject.toml
  • test/end2end/__init__.py
  • test/end2end/test_gui1_service_e2e.py
  • test/unittests/test_gui1_conformance.py
  • test/unittests/test_namespace.py
  • test/unittests/test_templates.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/gui1-service-conformance

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

The automated sentinels have completed their watch. 💂‍♂️

I've aggregated the results of the automated checks for this PR below.

🔨 Build Tests

Ensuring the scaffolding is removed and the build is clean. 🧹

✅ All versions pass

Python Build Install
3.10
3.11
3.12
3.13
3.14

🔌 Skill Tests (ovoscope)

I've checked the skill's 'documentation' vs its 'behavior'. 📖

12/12 passed

TestFrameVocabularyDualAccept — 3/3
TestNamespaceLifecycle — 1/1
TestPerSessionRouting — 3/3
TestProducerWireRoundTrip — 3/3
TestSystemPrefixGate — 2/2

🚌 Bus Coverage

Quantifying the thoroughness of our end-to-end signal tracking. 🧮

⚠️ Bus coverage report unavailable — check the job log.


The silent partner in your development journey 👥

JarbasAl added a commit that referenced this pull request Jun 28, 2026
Adds test/end2end/test_gui1_service_e2e.py: boots the real NamespaceManager
on a real bus (render websocket mocked) and drives the full
producer -> bus -> service path with the installed GUIInterface producer,
capturing the gui.* wire via ovoscope's GUICaptureSession. Asserts the GUI-1
clauses #117 implements end to end:

- §2.3/§4.1/§4.2 producer wire reaches the service; __from rides every
  producer message and reserved keys never become namespace session data
- §3.2/§4.2/§8.3 SYSTEM_ prefix gates a template intent; a non-SYSTEM_ first
  page is the legacy custom-page path, not a template
- §3.1/§8.1 the service dual-accepts legacy (SYSTEM_TextFrame) and spec
  (SYSTEM_text) frame names; spec names resolve to the legacy render resource
- §4.3/§5.1/§8.3 independent namespace stack per session_id; absent session
  defaults to the on-device 'default'; clear is session-scoped
- §4.3 lifecycle: gui.namespace.removed on the core bus on clear

Wires the gh-automations ovoscope.yml reusable workflow (test_path=test/end2end)
and a [test] extra pulling ovoscope>=1.0.2a1 with the GUI-1 prerelease-floor
pins (bus-client>=2.5.1a1, spec-tools>=0.17.3a1, utils>=0.12.0a1) so pip
resolves them with no --pre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
JarbasAl and others added 3 commits July 4, 2026 01:35
…b, per-session stacks)

Bring the GUI service into line with the service-side MUSTs of OVOS-GUI-1
without disturbing the legacy QML/WebSocket render path.

§3.2/§4.2/§8.3 SYSTEM_ prefix gate
  New ovos_gui/templates.py is the single source of truth for the closed
  SYSTEM_* vocabulary (§3.4). handle_show_page recognises a template intent
  by the SYSTEM_ prefix; a non-SYSTEM_ first page name is logged as the
  deployment-specific legacy custom-QML path, never treated as a template.

§3.1/§8.1 frame vocabulary (dual accept)
  The service now accepts BOTH the legacy CamelCase frame names
  (SYSTEM_TextFrame, ...) and the GUI-1 spec names (SYSTEM_text, ...). A spec
  name is resolved to the legacy QML resource the current render backends
  ship, so producers (ovos-bus-client) can migrate to spec names without
  breaking rendering.

§4.3/§5.1/§8.3 per-session namespace stacks
  State is partitioned per session_id via GUISession, read from
  context.session and defaulting to the reserved "default" id. Each session
  owns an independent namespace stack so sessions cannot collide. The legacy
  single-screen QML transport maps to the "default" session; the
  loaded_namespaces / active_namespaces / remove_namespace_timers attributes
  proxy to it, so bus.py synchronize() and existing behaviour are unchanged.

Tests
  test_templates.py (vocab + prefix gate + alias maps) and
  test_gui1_conformance.py (real NamespaceManager on a FakeBus: SYSTEM_ gate,
  legacy/spec dual-accept, per-session isolation of show/value.set/clear).
  111 passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pin prerelease floors resolved by pip without --pre:
  ovos_bus_client  >=2.2.0a1 -> >=2.5.1a1,<3.0.0
  ovos-spec-tools  >=0.9.0a1 -> >=0.16.1a2,<1.0.0
GUI-1 conformance imports ovos_spec_tools.SpecMessage; raise the floor to
the documented baseline that carries the SpecMessage registry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds test/end2end/test_gui1_service_e2e.py: boots the real NamespaceManager
on a real bus (render websocket mocked) and drives the full
producer -> bus -> service path with the installed GUIInterface producer,
capturing the gui.* wire via ovoscope's GUICaptureSession. Asserts the GUI-1
clauses #117 implements end to end:

- §2.3/§4.1/§4.2 producer wire reaches the service; __from rides every
  producer message and reserved keys never become namespace session data
- §3.2/§4.2/§8.3 SYSTEM_ prefix gates a template intent; a non-SYSTEM_ first
  page is the legacy custom-page path, not a template
- §3.1/§8.1 the service dual-accepts legacy (SYSTEM_TextFrame) and spec
  (SYSTEM_text) frame names; spec names resolve to the legacy render resource
- §4.3/§5.1/§8.3 independent namespace stack per session_id; absent session
  defaults to the on-device 'default'; clear is session-scoped
- §4.3 lifecycle: gui.namespace.removed on the core bus on clear

Wires the gh-automations ovoscope.yml reusable workflow (test_path=test/end2end)
and a [test] extra pulling ovoscope>=1.0.2a1 with the GUI-1 prerelease-floor
pins (bus-client>=2.5.1a1, spec-tools>=0.17.3a1, utils>=0.12.0a1) so pip
resolves them with no --pre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@JarbasAl
JarbasAl force-pushed the feat/gui1-service-conformance branch from 8ebd8b3 to 4c773e0 Compare July 4, 2026 00:37
Concurrent NamespaceManager instances sharing one XDG cache (e.g. ovoscope
parallel xdist workers) raced between the exists/rmtree/copytree steps and
crashed with FileExistsError on $GUI_CACHE_PATH/system. Copy with
dirs_exist_ok=True so refreshing the bundled system resources is idempotent
and never collides.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JarbasAl
JarbasAl marked this pull request as ready for review July 16, 2026 07:21
bus-client PR #238 makes producers omit __idle from gui.page.show
when unset. handle_show_page hard-indexed message.data["__idle"],
raising KeyError and crashing the handler (via the pyee executor)
whenever a conformant producer omitted the key. Per §4.3 an absent
__idle means "use the namespace default", not an error.

Switch both reads to .get("__idle") so the existing
_parse_persistence default-30s branch takes over. __from and
page_names remain required and are still validated up front by
_validate_page_message (logged error, no exception) - only the
optional __idle key was unguarded.
@JarbasAl
JarbasAl marked this pull request as draft July 24, 2026 00:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant