Files
computer-playbook/tests/integration/roles/dashboard/test_integration_scope.py
T
kevinveenbirkenbachandClaude Opus 5 53c120aaac refactor(dsk): rename the desktop tier and give its agents a category
Every desk- role becomes dsk-, and the three coding agents move to dsk-gnt- under a category of their own. The category carries a title, hanzi and icon but no invokable flag: an invokable child under an invokable parent is rejected outright, which is also why web itself is not invokable.

Three things a search and replace would have broken without a sound. get_entity_name now strips dsk-gnt-, so the primary entity keys agnt-claude, agnt-cursor and agnt-pi no longer resolved and all three roles dropped out of the CI plan. Eighteen run_once guards carried the old name against a convention its own test enforces. DESKTOP_ROLE_PREFIX in the addon spec lint is a literal, so the desktop exemption it grants would have matched nothing and the lint would have moved to the wrong rule in silence.

docs/requirements/035 is not a historical document but a register a lint holds to every role with an application_id, so its list carries the new ids. In 026 only the links move; the prose stays as a statement about the tree at the time it was written.

Measured against the plan with the rename normalised, the deployed role#variant set is unchanged. A stale id in an inventory is caught before any deploy starts: the pre-flight validator reports Invalid group 'desk-firefox' (not in application_ids).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 22:15:41 +02:00

114 lines
4.2 KiB
Python

"""Guard: only ``web-*`` roles may integrate the dashboard.
A role "integrates" the dashboard when its ``meta/services.yml`` (or
any entry in ``meta/variants.yml``) declares a ``dashboard:`` block
whose ``enabled`` or ``shared`` flag is truthy according to the
canonical helper :func:`utils.roles.applications.services.registry.is_explicit_truth`.
Truthy means either the literal Python ``True`` *or* the dynamic Jinja
form ``"{{ '<role>' in group_names }}"``. Both produce a runtime tile
in the dashboard grid, so both are forbidden for non-``web-*`` roles.
Why
---
The dashboard is the user-facing tile grid. Only web-facing surfaces
belong in it. A system role (``sys-*``), desktop role (``dsk-*``),
service role (``svc-*``), or driver role (``drv-*``) embedding the
dashboard would spam the tile grid with non-clickable infrastructure
entries.
A non-``web-*`` role MAY still ship a ``dashboard:`` block with
*explicitly disabled* flags (``enabled: false`` / ``shared: false``)
- that's a static "we know this service exists in the registry but
this role does not contribute a tile" declaration and is fine.
"""
from __future__ import annotations
import unittest
from typing import TYPE_CHECKING, Any
from utils.cache.yaml import load_yaml_any
from utils.roles.applications.services.registry import is_explicit_truth
from utils.roles.mapping import ROLE_FILE_META_SERVICES, ROLE_FILE_META_VARIANTS
from . import PROJECT_ROOT
if TYPE_CHECKING:
from pathlib import Path
ROLES_DIR = PROJECT_ROOT / "roles"
DASHBOARD_KEY = "dashboard"
def _load_yaml(path: Path) -> Any:
if not path.is_file():
return None
return load_yaml_any(str(path), default_if_missing=None)
def _dashboard_block_violates(block: Any) -> bool:
"""Return True iff the ``dashboard:`` mapping has a truthy
``enabled`` or ``shared`` flag."""
if not isinstance(block, dict):
return False
return is_explicit_truth(block.get("enabled")) or is_explicit_truth(
block.get("shared")
)
def _find_services_violation(role_dir: Path) -> str | None:
services_yaml = _load_yaml(role_dir / ROLE_FILE_META_SERVICES)
if not isinstance(services_yaml, dict):
return None
if _dashboard_block_violates(services_yaml.get(DASHBOARD_KEY)):
return f"{role_dir.name}: meta/services.yml declares `dashboard:` with a truthy enabled/shared flag"
return None
def _find_variants_violations(role_dir: Path) -> list[str]:
variants_yaml = _load_yaml(role_dir / ROLE_FILE_META_VARIANTS)
if not isinstance(variants_yaml, list):
return []
offenders: list[str] = []
for index, entry in enumerate(variants_yaml):
if not isinstance(entry, dict):
continue
services_override = entry.get("services")
if not isinstance(services_override, dict):
continue
if _dashboard_block_violates(services_override.get(DASHBOARD_KEY)):
offenders.append(
f"{role_dir.name}: meta/variants.yml[{index}].services.dashboard "
f"has a truthy enabled/shared flag"
)
return offenders
class TestDashboardIntegrationScope(unittest.TestCase):
def test_only_web_roles_integrate_dashboard(self) -> None:
offenders: list[str] = []
for role_dir in sorted(p for p in ROLES_DIR.iterdir() if p.is_dir()):
if role_dir.name.startswith("web-"):
continue
services_violation = _find_services_violation(role_dir)
if services_violation is not None:
offenders.append(services_violation)
offenders.extend(_find_variants_violations(role_dir))
if offenders:
self.fail(
"Non-web roles MUST NOT integrate the dashboard via a "
"truthy `enabled`/`shared` flag (literal `true` or the "
"`'<role>' in group_names` Jinja form). The dashboard tile "
"grid is reserved for web-facing surfaces. "
"Either drop the `dashboard:` block, or set both flags to "
"`false` if the registry declaration is intentional:\n"
+ "\n".join(f" - {o}" for o in offenders)
)
if __name__ == "__main__": # pragma: no cover
unittest.main()