Skip to content

feat(presets): split control-assistant tiers by role, surface, and panels - #573

Merged
thellert merged 4 commits into
mainfrom
fix/preset-persona-roles
Aug 12, 2026
Merged

feat(presets): split control-assistant tiers by role, surface, and panels#573
thellert merged 4 commits into
mainfrom
fix/preset-persona-roles

Conversation

@thellert

Copy link
Copy Markdown
Collaborator

What

Redesigns the control-assistant tutorial's two-user personas so the tiers are coherent end to end:

alice bob
persona readwrite (was readonly) readonly (was readwrite)
control_system.writes_enabled true false
web.ui_mode expert simple
panels all 6, incl. EVENTS + BLUESKY 4 — built without the two write-oriented panels
tab title Control Room (Alice) Read-Only View (Bob)
ports 9091 (unchanged) 9092 (unchanged)

Both terminals default to the main theme pinned light (web.theme: light).

Why the panels move instead of being disabled

web.panels.<id>.enabled: false is checked only for builtin panel ids — URL/iframe panels (EVENTS, BLUESKY) are appended unconditionally, so the key is inert for exactly these two. And a persona delta cannot subtract inherited config: keys (config is deliberately non-excludable). The one mechanism that makes the panels genuinely absent from the read-only build is declaring them only in the readwrite persona. The base project still gets both panels via the dispatch/bluesky-panels injector defaults; its EVENTS entry loses the health_endpoint/env-overridable URL the explicit declaration carried, which only affects a host-run osprey web from the base project directory.

Contract updates

  • Preset hashes re-pinned (all three moved) — deliberately deploy-visible: a rebuilt readonly project loses two tabs, gains the simple surface, and the roster swaps which port is write-armed, so the staleness advisory firing on already-deployed projects is the correct signal.
  • Persona contract test widened: test_personas_differ_only_on_writes_enabled becomes test_personas_differ_only_on_the_tier_contract, asserting the three axes (write switch, ui_mode, readwrite-only panel keys) and then wholesale equality, so any fourth drift still fails.
  • Docs: the multi-user guide's roster example, persona narrative, and comparison table now match what the preset ships (the roster example had also drifted — it still showed a bare-string alice entry); the landing-page screenshot is regenerated from a live deploy of the new shape.

Testing

Feature-scoped suites pass locally (120 tests: persona presets, preset render, hash pin, persona emission, profile roundtrip) plus ruff on the changed files. Full suite left to CI.

…nels

The tutorial roster now ships alice as the write-capable operator and bob
as the read-only viewer, each with a role-naming browser tab title. The
personas pin the full tier contract, not just the write switch: expert vs
simple web surface, and the EVENTS/BLUESKY panel declarations move from
the shared base into the readwrite persona so the read-only terminal is
built without them (a persona delta cannot subtract inherited config, and
enabled: false is inert for URL panels). Both terminals default to the
main theme in light mode.

Preset content hashes are re-pinned knowingly; the persona contract test
asserts the three-axis tier boundary wholesale, and the multi-user guide
and landing screenshot track the shipped shape.
assert rw_cfg.pop(WRITES_KEY) is True
# With the axis key removed, the rendered config overrides are identical.
# Axis 2 — surface: chat-first for the viewer, full dock for the operator.
assert ro_cfg.pop(UI_MODE_KEY) == "simple"
# With the axis key removed, the rendered config overrides are identical.
# Axis 2 — surface: chat-first for the viewer, full dock for the operator.
assert ro_cfg.pop(UI_MODE_KEY) == "simple"
assert rw_cfg.pop(UI_MODE_KEY) == "expert"
The static preset gate requires every web_panels id to be a builtin or be
backed by a web.panels.<id>.url override in the same resolved preset. With
the URL overrides now living in the readwrite persona, the events/bluesky
list entries follow them there: the base and readonly presets no longer
list panels they do not declare, and the readwrite persona lists exactly
the two it does. The artifact-parity test now pins panels as the one
deliberate list difference; preset hashes re-pinned accordingly.
The events-panel guard now checks the readwrite persona, where the panel
id and its URL override live, and asserts the base carries neither. The
emitted-delta comment check pins phrases as they flow in the current
readonly preset prose.
# Conflicts:
#	tests/cli/test_preset_hash_pin.py
@thellert
thellert merged commit 5d1663c into main Aug 12, 2026
44 checks passed
@thellert
thellert deleted the fix/preset-persona-roles branch August 12, 2026 16:58
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