Spaces:
Running
Running
muze: Gallery/Showcase tier on every archetype; layout routes on tier before confidence; main-tabs switch defaults ON
Browse filesLeadership 2026-09-22: Gallery-grade Muze (declarative grammar, editable in
Muze Studio) may sit on prod main tabs; Showcase-grade programs (KPI card
boards, risk matrix, ridgelines, year-in-sales) only on the 4th tab beside
the table. All 16 shipped archetypes are Gallery grade; each now declares
tier=GALLERY, tier_of()/muze_tier() expose it, and _fill_showcase sends any
SHOWCASE-tier or untagged Muze tile to the 4th tab regardless of rating.
MUZE_ENGINE_MAIN_TABS: unset now means ON (the OFF default was the 'keep Muze
off prod' rule that leadership lifted); 'false' remains the emergency off.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
demoprep_app/liveboards/golden_layout.py
CHANGED
|
@@ -60,27 +60,25 @@ MUZE_SHOWCASE_MAX = 3 # boone 2026-09-03: 'a little more Muze'
|
|
| 60 |
# The data-first Muze engine (Sept 10) on the exec/manager tabs is a PER-SPACE
|
| 61 |
# switch, read from the environment at build time:
|
| 62 |
#
|
| 63 |
-
#
|
| 64 |
-
# archetype are drawn as Muze on
|
| 65 |
-
#
|
| 66 |
-
#
|
| 67 |
-
# 2026-09-15;
|
| 68 |
-
#
|
| 69 |
#
|
| 70 |
-
#
|
| 71 |
-
#
|
| 72 |
-
#
|
| 73 |
-
#
|
| 74 |
-
#
|
| 75 |
-
# build runs everywhere and the Space VARIABLE (not a secret) decides: set on
|
| 76 |
-
# test-mcp, absent on prod (boone 2026-09-21, Agent Q review action item).
|
| 77 |
#
|
| 78 |
# The state is never invisible: config_check reports it per Space, and every
|
| 79 |
# build stamps muze_main_tabs_state() into its change list. An unreadable value
|
| 80 |
# is refused, not guessed - a typo must not quietly turn Muze on in prod.
|
| 81 |
MUZE_ENGINE_MAIN_TABS_VAR = "MUZE_ENGINE_MAIN_TABS"
|
| 82 |
-
_FLAG_TRUE = ("1", "true", "on", "yes")
|
| 83 |
-
_FLAG_FALSE = ("
|
| 84 |
|
| 85 |
|
| 86 |
def muze_main_tabs_enabled() -> bool:
|
|
@@ -99,7 +97,7 @@ def muze_main_tabs_state() -> str:
|
|
| 99 |
"""One line for change lists and config_check: the value AND where it came from."""
|
| 100 |
raw = (os.getenv(MUZE_ENGINE_MAIN_TABS_VAR) or "").strip()
|
| 101 |
if not raw:
|
| 102 |
-
return f"Muze on main tabs:
|
| 103 |
return (f"Muze on main tabs: {'ON' if muze_main_tabs_enabled() else 'OFF'} "
|
| 104 |
f"({MUZE_ENGINE_MAIN_TABS_VAR}={raw})")
|
| 105 |
MUZE_SHOWCASE_MIN = 2 # if surplus yields fewer, upgrade the retained heatmap too
|
|
@@ -1132,11 +1130,15 @@ def _fill_showcase(
|
|
| 1132 |
# 1. Custom and exotic visuals — the reason this tab exists. A Muze tile the
|
| 1133 |
# engine drew with HIGH confidence is a first-class chart and stays where
|
| 1134 |
# its role put it; medium/low confidence and hand-made custom charts come here.
|
| 1135 |
-
from demoprep_app.liveboards.muze_engine import muze_confidence
|
| 1136 |
# No cap here: a medium/low Muze tile must NEVER stay on a main tab. Run
|
| 1137 |
# 0910_125157 put a medium lollipop on Tixr's exec tab because the 5th one
|
| 1138 |
# overflowed the showcase target.
|
| 1139 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1140 |
10**6)
|
| 1141 |
|
| 1142 |
# 2. Exactly one table.
|
|
|
|
| 60 |
# The data-first Muze engine (Sept 10) on the exec/manager tabs is a PER-SPACE
|
| 61 |
# switch, read from the environment at build time:
|
| 62 |
#
|
| 63 |
+
# unset / true answers whose shape fits a high-confidence
|
| 64 |
+
# GALLERY-tier archetype are drawn as Muze on
|
| 65 |
+
# the main tabs (the default)
|
| 66 |
+
# MUZE_ENGINE_MAIN_TABS=false the engine is skipped and the main tabs carry
|
| 67 |
+
# native charts as prod did 2026-09-15..22;
|
| 68 |
+
# Muze appears only on the showcase tab
|
| 69 |
#
|
| 70 |
+
# ON is the default since 2026-09-22: leadership approved Gallery-grade Muze on
|
| 71 |
+
# prod main tabs (Showcase-grade stays on the 4th tab — see muze_archetypes
|
| 72 |
+
# GALLERY/SHOWCASE). The switch is kept as the emergency off: the 2026-09-15
|
| 73 |
+
# scare (a stakeholder asked that one-day-old Muze stay off prod) is exactly
|
| 74 |
+
# when it earns its keep, and a Space VARIABLE flips it without a deploy.
|
|
|
|
|
|
|
| 75 |
#
|
| 76 |
# The state is never invisible: config_check reports it per Space, and every
|
| 77 |
# build stamps muze_main_tabs_state() into its change list. An unreadable value
|
| 78 |
# is refused, not guessed - a typo must not quietly turn Muze on in prod.
|
| 79 |
MUZE_ENGINE_MAIN_TABS_VAR = "MUZE_ENGINE_MAIN_TABS"
|
| 80 |
+
_FLAG_TRUE = ("", "1", "true", "on", "yes") # unset -> ON (default since 2026-09-22)
|
| 81 |
+
_FLAG_FALSE = ("0", "false", "off", "no")
|
| 82 |
|
| 83 |
|
| 84 |
def muze_main_tabs_enabled() -> bool:
|
|
|
|
| 97 |
"""One line for change lists and config_check: the value AND where it came from."""
|
| 98 |
raw = (os.getenv(MUZE_ENGINE_MAIN_TABS_VAR) or "").strip()
|
| 99 |
if not raw:
|
| 100 |
+
return f"Muze on main tabs: ON ({MUZE_ENGINE_MAIN_TABS_VAR} unset - default)"
|
| 101 |
return (f"Muze on main tabs: {'ON' if muze_main_tabs_enabled() else 'OFF'} "
|
| 102 |
f"({MUZE_ENGINE_MAIN_TABS_VAR}={raw})")
|
| 103 |
MUZE_SHOWCASE_MIN = 2 # if surplus yields fewer, upgrade the retained heatmap too
|
|
|
|
| 1130 |
# 1. Custom and exotic visuals — the reason this tab exists. A Muze tile the
|
| 1131 |
# engine drew with HIGH confidence is a first-class chart and stays where
|
| 1132 |
# its role put it; medium/low confidence and hand-made custom charts come here.
|
| 1133 |
+
from demoprep_app.liveboards.muze_engine import muze_confidence, muze_tier
|
| 1134 |
# No cap here: a medium/low Muze tile must NEVER stay on a main tab. Run
|
| 1135 |
# 0910_125157 put a medium lollipop on Tixr's exec tab because the 5th one
|
| 1136 |
# overflowed the showcase target.
|
| 1137 |
+
# Tier first (leadership 2026-09-22): SHOWCASE-grade Muze — and any Muze
|
| 1138 |
+
# tile without a recognised @archetype — goes here regardless of rating.
|
| 1139 |
+
# GALLERY-grade then routes on confidence as before.
|
| 1140 |
+
take(lambda v: _chart_type(v) in SHOWCASE_CHART_TYPES
|
| 1141 |
+
and (muze_tier(v) == "showcase" or muze_confidence(v) != "high"),
|
| 1142 |
10**6)
|
| 1143 |
|
| 1144 |
# 2. Exactly one table.
|
demoprep_app/liveboards/muze_archetypes.py
CHANGED
|
@@ -37,6 +37,15 @@ from demoprep_app.liveboards.muze_templates import (
|
|
| 37 |
|
| 38 |
HIGH, MEDIUM, LOW = "high", "medium", "low"
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
# --------------------------------------------------------------------------- safety skeleton
|
| 41 |
_PRELUDE = """\
|
| 42 |
// @archetype __ARCHETYPE__
|
|
@@ -364,13 +373,13 @@ _STACKED_AREA = """\
|
|
| 364 |
# slots: engine slot -> JS constant. Engine slots: measure, measure2, dim, dim2, date.
|
| 365 |
ARCHETYPES: Dict[str, Dict[str, Any]] = {
|
| 366 |
"bar_ranking": {
|
| 367 |
-
"body": _BAR, "confidence": HIGH, "family": "Ranking",
|
| 368 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 369 |
"max_card": {"dim": 15},
|
| 370 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
| 371 |
},
|
| 372 |
"hbar_ranking": {
|
| 373 |
-
"body": _HBAR, "confidence": HIGH, "family": "Ranking",
|
| 374 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 375 |
"max_card": {"dim": 15},
|
| 376 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
|
@@ -379,25 +388,25 @@ ARCHETYPES: Dict[str, Dict[str, Any]] = {
|
|
| 379 |
# MEDIUM (showcase) 2026-09-16: head size solved — Muze point `size.value`
|
| 380 |
# is a fraction of the band (1/8/18/90 all saturated at ~100px on 09-10;
|
| 381 |
# 0.3 measured ~35px on the scatter). 0.15 here; promote after a real render.
|
| 382 |
-
"body": _LOLLIPOP, "confidence": MEDIUM, "family": "Ranking",
|
| 383 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 384 |
"max_card": {"dim": 15},
|
| 385 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
| 386 |
},
|
| 387 |
"stacked_bar": {
|
| 388 |
-
"body": _STACKED_BAR, "confidence": HIGH, "family": "Part of a Whole",
|
| 389 |
"needs": {"measures": 1, "dims": 2, "date": False},
|
| 390 |
"max_card": {"dim": 12, "dim2": 6},
|
| 391 |
"slots": {"dim": "D_FIELD", "dim2": "C_FIELD", "measure": "M_FIELD"},
|
| 392 |
},
|
| 393 |
"multi_line": {
|
| 394 |
-
"body": _MULTI_LINE, "confidence": HIGH, "family": "Trend Analysis",
|
| 395 |
"needs": {"measures": 1, "dims": 1, "date": True},
|
| 396 |
"max_card": {"dim": 6},
|
| 397 |
"slots": {"date": "T_FIELD", "dim": "C_FIELD", "measure": "M_FIELD"},
|
| 398 |
},
|
| 399 |
"bullet": {
|
| 400 |
-
"body": _BULLET, "confidence": HIGH, "family": "Ranking",
|
| 401 |
"needs": {"measures": 2, "dims": 1, "date": False, "target": True},
|
| 402 |
"max_card": {"dim": 12},
|
| 403 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD", "measure2": "T_FIELD"},
|
|
@@ -406,7 +415,7 @@ ARCHETYPES: Dict[str, Dict[str, Any]] = {
|
|
| 406 |
# MEDIUM on purpose: the surplus-heatmap upgrade uses it, and surplus
|
| 407 |
# heatmaps belong on the showcase tab. The engine's two-dim fallback to it
|
| 408 |
# therefore lands there too; stacked_bar (few colours) is the main-tab pick.
|
| 409 |
-
"body": None, "confidence": MEDIUM, "family": "Distribution", # drawn by muze_templates
|
| 410 |
"needs": {"measures": 1, "dims": 2, "date": False},
|
| 411 |
"max_card": {"dim": 12, "dim2": 12},
|
| 412 |
"slots": {"dim": "x", "dim2": "y", "measure": "measure"},
|
|
@@ -416,7 +425,7 @@ ARCHETYPES: Dict[str, Dict[str, Any]] = {
|
|
| 416 |
# category, no target) is what 15 native tiles on main tabs were on 2026-09-10.
|
| 417 |
# HIGH: rendered and inspected 2026-09-10 (GridPuls "DR Capacity by Participant":
|
| 418 |
# bars left axis, line right axis, compact ticks, both axes titled).
|
| 419 |
-
"body": _DUAL_AXIS, "confidence": HIGH, "family": "Multivariate",
|
| 420 |
"needs": {"measures": 2, "dims": 1, "date": False},
|
| 421 |
"max_card": {"dim": 12},
|
| 422 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD", "measure2": "M2_FIELD"},
|
|
@@ -424,7 +433,7 @@ ARCHETYPES: Dict[str, Dict[str, Any]] = {
|
|
| 424 |
"donut": {
|
| 425 |
# HIGH 2026-09-16: Tixr "Tickets by Channel" (6 slices, legend bottom, all
|
| 426 |
# labels visible). The engine only offers it for <=6 additive slices.
|
| 427 |
-
"body": _DONUT, "confidence": HIGH, "family": "Part of a Whole",
|
| 428 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 429 |
"max_card": {"dim": 6},
|
| 430 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
|
@@ -435,7 +444,7 @@ ARCHETYPES: Dict[str, Dict[str, Any]] = {
|
|
| 435 |
"pareto": {
|
| 436 |
# HIGH 2026-09-16: Tixr "Top 10 Clients by Revenue" — bars in rank order,
|
| 437 |
# cumulative-share line on a % axis, compact ticks.
|
| 438 |
-
"body": _PARETO, "confidence": HIGH, "family": "Ranking",
|
| 439 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 440 |
"max_card": {"dim": 15},
|
| 441 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
|
@@ -443,7 +452,7 @@ ARCHETYPES: Dict[str, Dict[str, Any]] = {
|
|
| 443 |
"polar_area": {
|
| 444 |
# MEDIUM (showcase): the rose draws (True "Pipeline by Client Segment") but
|
| 445 |
# small slices vanish — a showcase piece, not an executive read.
|
| 446 |
-
"body": _POLAR_AREA, "confidence": MEDIUM, "family": "Distribution",
|
| 447 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 448 |
"max_card": {"dim": 8},
|
| 449 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
|
@@ -451,20 +460,20 @@ ARCHETYPES: Dict[str, Dict[str, Any]] = {
|
|
| 451 |
"scatter": {
|
| 452 |
# MEDIUM (showcase): renders (Tixr "Ticket & Fee Revenue by Vertical", size
|
| 453 |
# 0.3) but four points is a sparse tile; earns main tabs once seen on more data.
|
| 454 |
-
"body": _SCATTER, "confidence": MEDIUM, "family": "Correlation",
|
| 455 |
"needs": {"measures": 2, "dims": 1, "date": False},
|
| 456 |
"max_card": {"dim": 15},
|
| 457 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD", "measure2": "M2_FIELD"},
|
| 458 |
},
|
| 459 |
"bar_tick": {
|
| 460 |
-
"body": _BAR_TICK, "confidence": MEDIUM, "family": "Ranking",
|
| 461 |
"needs": {"measures": 2, "dims": 1, "date": False},
|
| 462 |
"max_card": {"dim": 10},
|
| 463 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD", "measure2": "M2_FIELD"},
|
| 464 |
},
|
| 465 |
"stacked_bar_100": {
|
| 466 |
# HIGH 2026-09-16: Tixr "Tickets by Genre" x Region — 0-100% axis, legend bottom.
|
| 467 |
-
"body": _STACKED_BAR_100, "confidence": HIGH, "family": "Part of a Whole",
|
| 468 |
"needs": {"measures": 1, "dims": 2, "date": False},
|
| 469 |
"max_card": {"dim": 12, "dim2": 6},
|
| 470 |
"slots": {"dim": "D_FIELD", "dim2": "C_FIELD", "measure": "M_FIELD"},
|
|
@@ -472,14 +481,14 @@ ARCHETYPES: Dict[str, Dict[str, Any]] = {
|
|
| 472 |
"crosstab_bar": {
|
| 473 |
# MEDIUM (showcase): clean trellis (Tixr "Revenue by Sales Channel" x Payment
|
| 474 |
# Category) but tall; a third-width tile would cramp it.
|
| 475 |
-
"body": _CROSSTAB_BAR, "confidence": MEDIUM, "family": "Ranking",
|
| 476 |
"needs": {"measures": 1, "dims": 2, "date": False},
|
| 477 |
"max_card": {"dim": 10, "dim2": 5},
|
| 478 |
"slots": {"dim": "D_FIELD", "dim2": "C_FIELD", "measure": "M_FIELD"},
|
| 479 |
},
|
| 480 |
"stacked_area": {
|
| 481 |
# HIGH 2026-09-16: True "Monthly Revenue by Service Line", 18 months, month ticks.
|
| 482 |
-
"body": _STACKED_AREA, "confidence": HIGH, "family": "Part of a Whole",
|
| 483 |
"needs": {"measures": 1, "dims": 1, "date": True},
|
| 484 |
"max_card": {"dim": 6},
|
| 485 |
"slots": {"date": "T_FIELD", "dim": "C_FIELD", "measure": "M_FIELD"},
|
|
@@ -489,6 +498,12 @@ ARCHETYPES: Dict[str, Dict[str, Any]] = {
|
|
| 489 |
TARGET_WORDS = ("target", "goal", "budget", "plan", "quota", "forecast", "benchmark")
|
| 490 |
|
| 491 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 492 |
def confidence_of(archetype: str) -> str:
|
| 493 |
return ARCHETYPES[archetype]["confidence"]
|
| 494 |
|
|
|
|
| 37 |
|
| 38 |
HIGH, MEDIUM, LOW = "high", "medium", "low"
|
| 39 |
|
| 40 |
+
# Two tiers of Muze, as ThoughtSpot itself publishes them (leadership 2026-09-22):
|
| 41 |
+
# GALLERY — Muze Gallery grammar: rows/columns/color, one or two layers, a
|
| 42 |
+
# dozen lines an SE can edit in Muze Studio. Allowed on tabs 1-3.
|
| 43 |
+
# SHOWCASE — Showcase-grade programs (KPI card boards, risk matrix, ridgelines,
|
| 44 |
+
# "year in sales"): transforms, several canvases, custom DOM. Only
|
| 45 |
+
# ever on the 4th tab, next to the table and the experiments.
|
| 46 |
+
# Every archetype declares its tier; the layout routes on it BEFORE confidence.
|
| 47 |
+
GALLERY, SHOWCASE = "gallery", "showcase"
|
| 48 |
+
|
| 49 |
# --------------------------------------------------------------------------- safety skeleton
|
| 50 |
_PRELUDE = """\
|
| 51 |
// @archetype __ARCHETYPE__
|
|
|
|
| 373 |
# slots: engine slot -> JS constant. Engine slots: measure, measure2, dim, dim2, date.
|
| 374 |
ARCHETYPES: Dict[str, Dict[str, Any]] = {
|
| 375 |
"bar_ranking": {
|
| 376 |
+
"body": _BAR, "confidence": HIGH, "family": "Ranking", "tier": GALLERY,
|
| 377 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 378 |
"max_card": {"dim": 15},
|
| 379 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
| 380 |
},
|
| 381 |
"hbar_ranking": {
|
| 382 |
+
"body": _HBAR, "confidence": HIGH, "family": "Ranking", "tier": GALLERY,
|
| 383 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 384 |
"max_card": {"dim": 15},
|
| 385 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
|
|
|
| 388 |
# MEDIUM (showcase) 2026-09-16: head size solved — Muze point `size.value`
|
| 389 |
# is a fraction of the band (1/8/18/90 all saturated at ~100px on 09-10;
|
| 390 |
# 0.3 measured ~35px on the scatter). 0.15 here; promote after a real render.
|
| 391 |
+
"body": _LOLLIPOP, "confidence": MEDIUM, "family": "Ranking", "tier": GALLERY,
|
| 392 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 393 |
"max_card": {"dim": 15},
|
| 394 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
| 395 |
},
|
| 396 |
"stacked_bar": {
|
| 397 |
+
"body": _STACKED_BAR, "confidence": HIGH, "family": "Part of a Whole", "tier": GALLERY,
|
| 398 |
"needs": {"measures": 1, "dims": 2, "date": False},
|
| 399 |
"max_card": {"dim": 12, "dim2": 6},
|
| 400 |
"slots": {"dim": "D_FIELD", "dim2": "C_FIELD", "measure": "M_FIELD"},
|
| 401 |
},
|
| 402 |
"multi_line": {
|
| 403 |
+
"body": _MULTI_LINE, "confidence": HIGH, "family": "Trend Analysis", "tier": GALLERY,
|
| 404 |
"needs": {"measures": 1, "dims": 1, "date": True},
|
| 405 |
"max_card": {"dim": 6},
|
| 406 |
"slots": {"date": "T_FIELD", "dim": "C_FIELD", "measure": "M_FIELD"},
|
| 407 |
},
|
| 408 |
"bullet": {
|
| 409 |
+
"body": _BULLET, "confidence": HIGH, "family": "Ranking", "tier": GALLERY,
|
| 410 |
"needs": {"measures": 2, "dims": 1, "date": False, "target": True},
|
| 411 |
"max_card": {"dim": 12},
|
| 412 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD", "measure2": "T_FIELD"},
|
|
|
|
| 415 |
# MEDIUM on purpose: the surplus-heatmap upgrade uses it, and surplus
|
| 416 |
# heatmaps belong on the showcase tab. The engine's two-dim fallback to it
|
| 417 |
# therefore lands there too; stacked_bar (few colours) is the main-tab pick.
|
| 418 |
+
"body": None, "confidence": MEDIUM, "family": "Distribution", "tier": GALLERY, # drawn by muze_templates
|
| 419 |
"needs": {"measures": 1, "dims": 2, "date": False},
|
| 420 |
"max_card": {"dim": 12, "dim2": 12},
|
| 421 |
"slots": {"dim": "x", "dim2": "y", "measure": "measure"},
|
|
|
|
| 425 |
# category, no target) is what 15 native tiles on main tabs were on 2026-09-10.
|
| 426 |
# HIGH: rendered and inspected 2026-09-10 (GridPuls "DR Capacity by Participant":
|
| 427 |
# bars left axis, line right axis, compact ticks, both axes titled).
|
| 428 |
+
"body": _DUAL_AXIS, "confidence": HIGH, "family": "Multivariate", "tier": GALLERY,
|
| 429 |
"needs": {"measures": 2, "dims": 1, "date": False},
|
| 430 |
"max_card": {"dim": 12},
|
| 431 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD", "measure2": "M2_FIELD"},
|
|
|
|
| 433 |
"donut": {
|
| 434 |
# HIGH 2026-09-16: Tixr "Tickets by Channel" (6 slices, legend bottom, all
|
| 435 |
# labels visible). The engine only offers it for <=6 additive slices.
|
| 436 |
+
"body": _DONUT, "confidence": HIGH, "family": "Part of a Whole", "tier": GALLERY,
|
| 437 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 438 |
"max_card": {"dim": 6},
|
| 439 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
|
|
|
| 444 |
"pareto": {
|
| 445 |
# HIGH 2026-09-16: Tixr "Top 10 Clients by Revenue" — bars in rank order,
|
| 446 |
# cumulative-share line on a % axis, compact ticks.
|
| 447 |
+
"body": _PARETO, "confidence": HIGH, "family": "Ranking", "tier": GALLERY,
|
| 448 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 449 |
"max_card": {"dim": 15},
|
| 450 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
|
|
|
| 452 |
"polar_area": {
|
| 453 |
# MEDIUM (showcase): the rose draws (True "Pipeline by Client Segment") but
|
| 454 |
# small slices vanish — a showcase piece, not an executive read.
|
| 455 |
+
"body": _POLAR_AREA, "confidence": MEDIUM, "family": "Distribution", "tier": GALLERY,
|
| 456 |
"needs": {"measures": 1, "dims": 1, "date": False},
|
| 457 |
"max_card": {"dim": 8},
|
| 458 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD"},
|
|
|
|
| 460 |
"scatter": {
|
| 461 |
# MEDIUM (showcase): renders (Tixr "Ticket & Fee Revenue by Vertical", size
|
| 462 |
# 0.3) but four points is a sparse tile; earns main tabs once seen on more data.
|
| 463 |
+
"body": _SCATTER, "confidence": MEDIUM, "family": "Correlation", "tier": GALLERY,
|
| 464 |
"needs": {"measures": 2, "dims": 1, "date": False},
|
| 465 |
"max_card": {"dim": 15},
|
| 466 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD", "measure2": "M2_FIELD"},
|
| 467 |
},
|
| 468 |
"bar_tick": {
|
| 469 |
+
"body": _BAR_TICK, "confidence": MEDIUM, "family": "Ranking", "tier": GALLERY,
|
| 470 |
"needs": {"measures": 2, "dims": 1, "date": False},
|
| 471 |
"max_card": {"dim": 10},
|
| 472 |
"slots": {"dim": "D_FIELD", "measure": "M_FIELD", "measure2": "M2_FIELD"},
|
| 473 |
},
|
| 474 |
"stacked_bar_100": {
|
| 475 |
# HIGH 2026-09-16: Tixr "Tickets by Genre" x Region — 0-100% axis, legend bottom.
|
| 476 |
+
"body": _STACKED_BAR_100, "confidence": HIGH, "family": "Part of a Whole", "tier": GALLERY,
|
| 477 |
"needs": {"measures": 1, "dims": 2, "date": False},
|
| 478 |
"max_card": {"dim": 12, "dim2": 6},
|
| 479 |
"slots": {"dim": "D_FIELD", "dim2": "C_FIELD", "measure": "M_FIELD"},
|
|
|
|
| 481 |
"crosstab_bar": {
|
| 482 |
# MEDIUM (showcase): clean trellis (Tixr "Revenue by Sales Channel" x Payment
|
| 483 |
# Category) but tall; a third-width tile would cramp it.
|
| 484 |
+
"body": _CROSSTAB_BAR, "confidence": MEDIUM, "family": "Ranking", "tier": GALLERY,
|
| 485 |
"needs": {"measures": 1, "dims": 2, "date": False},
|
| 486 |
"max_card": {"dim": 10, "dim2": 5},
|
| 487 |
"slots": {"dim": "D_FIELD", "dim2": "C_FIELD", "measure": "M_FIELD"},
|
| 488 |
},
|
| 489 |
"stacked_area": {
|
| 490 |
# HIGH 2026-09-16: True "Monthly Revenue by Service Line", 18 months, month ticks.
|
| 491 |
+
"body": _STACKED_AREA, "confidence": HIGH, "family": "Part of a Whole", "tier": GALLERY,
|
| 492 |
"needs": {"measures": 1, "dims": 1, "date": True},
|
| 493 |
"max_card": {"dim": 6},
|
| 494 |
"slots": {"date": "T_FIELD", "dim": "C_FIELD", "measure": "M_FIELD"},
|
|
|
|
| 498 |
TARGET_WORDS = ("target", "goal", "budget", "plan", "quota", "forecast", "benchmark")
|
| 499 |
|
| 500 |
|
| 501 |
+
def tier_of(archetype: str) -> str:
|
| 502 |
+
"""GALLERY or SHOWCASE; unknown archetypes are treated as SHOWCASE (kept off the main tabs)."""
|
| 503 |
+
spec = ARCHETYPES.get(archetype)
|
| 504 |
+
return spec.get("tier", SHOWCASE) if spec else SHOWCASE
|
| 505 |
+
|
| 506 |
+
|
| 507 |
def confidence_of(archetype: str) -> str:
|
| 508 |
return ARCHETYPES[archetype]["confidence"]
|
| 509 |
|
demoprep_app/liveboards/muze_engine.py
CHANGED
|
@@ -227,6 +227,17 @@ def convert_to_muze(viz: Dict[str, Any], archetype: str, bindings: Dict[str, str
|
|
| 227 |
answer["display_mode"] = "CHART_MODE"
|
| 228 |
|
| 229 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
def muze_confidence(viz: Dict[str, Any]) -> Optional[str]:
|
| 231 |
"""Confidence of the archetype that drew a MUZE_STUDIO viz, or None if not Muze."""
|
| 232 |
name = archetype_of((viz.get("answer") or {}).get("chart") or {})
|
|
|
|
| 227 |
answer["display_mode"] = "CHART_MODE"
|
| 228 |
|
| 229 |
|
| 230 |
+
def muze_tier(viz: Dict[str, Any]) -> Optional[str]:
|
| 231 |
+
"""GALLERY / SHOWCASE for a MUZE_STUDIO viz, None if not Muze. Unknown or
|
| 232 |
+
hand-made Muze (no @archetype tag) is SHOWCASE: off the main tabs."""
|
| 233 |
+
chart = (viz.get("answer") or {}).get("chart") or {}
|
| 234 |
+
if chart.get("type") != MUZE_CHART_TYPE:
|
| 235 |
+
return None
|
| 236 |
+
from demoprep_app.liveboards.muze_archetypes import tier_of
|
| 237 |
+
name = archetype_of(chart)
|
| 238 |
+
return tier_of(name) if name else "showcase"
|
| 239 |
+
|
| 240 |
+
|
| 241 |
def muze_confidence(viz: Dict[str, Any]) -> Optional[str]:
|
| 242 |
"""Confidence of the archetype that drew a MUZE_STUDIO viz, or None if not Muze."""
|
| 243 |
name = archetype_of((viz.get("answer") or {}).get("chart") or {})
|
tests/test_muze_engine.py
CHANGED
|
@@ -210,3 +210,28 @@ def test_pareto_only_for_additive_measures():
|
|
| 210 |
assert "pareto" not in picks
|
| 211 |
total = _viz("V2", "Revenue by Provider", "[Revenue] [Provider Type]", ["Provider Type", "Total Revenue"])
|
| 212 |
assert "pareto" in {choose_archetype(_shape(total, {"Provider Type": 8}), i)[0] for i in range(12)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
assert "pareto" not in picks
|
| 211 |
total = _viz("V2", "Revenue by Provider", "[Revenue] [Provider Type]", ["Provider Type", "Total Revenue"])
|
| 212 |
assert "pareto" in {choose_archetype(_shape(total, {"Provider Type": 8}), i)[0] for i in range(12)}
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
def test_every_archetype_declares_a_tier_and_all_current_ones_are_gallery_grade():
|
| 216 |
+
from demoprep_app.liveboards.muze_archetypes import GALLERY, SHOWCASE, tier_of
|
| 217 |
+
for name, spec in ARCHETYPES.items():
|
| 218 |
+
assert spec.get("tier") in (GALLERY, SHOWCASE), name
|
| 219 |
+
assert tier_of(name) == GALLERY, f"{name}: every shipped archetype is Gallery grammar (leadership 2026-09-22)"
|
| 220 |
+
assert tier_of("something_new") == SHOWCASE # unknown stays off the main tabs
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def test_showcase_tier_muze_always_routes_to_the_fourth_tab():
|
| 224 |
+
"""A SHOWCASE-tier tile must leave the main tabs even if someone rates it HIGH."""
|
| 225 |
+
from demoprep_app.liveboards import muze_archetypes as MA
|
| 226 |
+
from demoprep_app.liveboards.golden_layout import _fill_showcase
|
| 227 |
+
viz = _viz("V1", "Revenue by Region", "[Revenue] [Region]", ["Region", "Total Revenue"])
|
| 228 |
+
convert_to_muze(viz, "bar_ranking", {"dim": "Region", "measure": "Total Revenue"})
|
| 229 |
+
saved = dict(MA.ARCHETYPES["bar_ranking"])
|
| 230 |
+
try:
|
| 231 |
+
MA.ARCHETYPES["bar_ranking"]["tier"] = MA.SHOWCASE # pretend it is a program
|
| 232 |
+
MA.ARCHETYPES["bar_ranking"]["confidence"] = HIGH
|
| 233 |
+
buckets = {"breakdown": ["V1"], "ranking": [], "detail": [], "driver": [], "trend": [], "showcase": []}
|
| 234 |
+
_fill_showcase(buckets, [viz])
|
| 235 |
+
assert buckets["showcase"] == ["V1"] and buckets["breakdown"] == []
|
| 236 |
+
finally:
|
| 237 |
+
MA.ARCHETYPES["bar_ranking"] = saved
|
tests/test_muze_main_tabs_flag.py
CHANGED
|
@@ -5,7 +5,8 @@ Run: pytest tests/test_muze_main_tabs_flag.py -q --noconftest
|
|
| 5 |
It used to be a module constant: True on test, False in the emergency prod
|
| 6 |
build. That meant test and prod could never run the same commit, so nothing
|
| 7 |
else could ship to prod without carrying Muze along. Now one build runs
|
| 8 |
-
everywhere and the Space variable decides.
|
|
|
|
| 9 |
stamped into every build's change list so it is never invisible.
|
| 10 |
"""
|
| 11 |
from __future__ import annotations
|
|
@@ -21,22 +22,23 @@ from demoprep_app.liveboards.golden_layout import (
|
|
| 21 |
|
| 22 |
|
| 23 |
class TestReading:
|
| 24 |
-
@pytest.mark.parametrize("raw", ["true", "TRUE", " True ", "1", "on", "yes"])
|
| 25 |
def test_true_spellings(self, monkeypatch, raw):
|
| 26 |
monkeypatch.setenv(MUZE_ENGINE_MAIN_TABS_VAR, raw)
|
| 27 |
assert muze_main_tabs_enabled() is True
|
| 28 |
|
| 29 |
-
@pytest.mark.parametrize("raw", ["false", "FALSE", "0", "off", "no"
|
| 30 |
def test_false_spellings(self, monkeypatch, raw):
|
| 31 |
monkeypatch.setenv(MUZE_ENGINE_MAIN_TABS_VAR, raw)
|
| 32 |
assert muze_main_tabs_enabled() is False
|
| 33 |
|
| 34 |
-
def
|
| 35 |
-
"""Default
|
|
|
|
| 36 |
monkeypatch.delenv(MUZE_ENGINE_MAIN_TABS_VAR, raising=False)
|
| 37 |
-
assert muze_main_tabs_enabled() is
|
| 38 |
state = muze_main_tabs_state()
|
| 39 |
-
assert "
|
| 40 |
|
| 41 |
def test_a_set_value_names_itself(self, monkeypatch):
|
| 42 |
monkeypatch.setenv(MUZE_ENGINE_MAIN_TABS_VAR, "true")
|
|
@@ -84,7 +86,7 @@ class TestGate:
|
|
| 84 |
return calls, changes
|
| 85 |
|
| 86 |
def test_off_skips_the_engine_and_stamps_off(self, monkeypatch):
|
| 87 |
-
monkeypatch.
|
| 88 |
calls, changes = self._run(monkeypatch)
|
| 89 |
assert calls == []
|
| 90 |
assert any(c.startswith("Muze on main tabs: OFF") for c in changes), changes
|
|
@@ -97,7 +99,7 @@ class TestGate:
|
|
| 97 |
|
| 98 |
def test_the_same_code_flips_with_the_environment(self, monkeypatch):
|
| 99 |
"""One commit, two Spaces: only the variable differs."""
|
| 100 |
-
monkeypatch.
|
| 101 |
off, _ = self._run(monkeypatch)
|
| 102 |
monkeypatch.setenv(MUZE_ENGINE_MAIN_TABS_VAR, "true")
|
| 103 |
on, _ = self._run(monkeypatch)
|
|
|
|
| 5 |
It used to be a module constant: True on test, False in the emergency prod
|
| 6 |
build. That meant test and prod could never run the same commit, so nothing
|
| 7 |
else could ship to prod without carrying Muze along. Now one build runs
|
| 8 |
+
everywhere and the Space variable decides. ON is the default since 2026-09-22
|
| 9 |
+
(leadership approved Gallery-grade Muze on prod main tabs), and the state is
|
| 10 |
stamped into every build's change list so it is never invisible.
|
| 11 |
"""
|
| 12 |
from __future__ import annotations
|
|
|
|
| 22 |
|
| 23 |
|
| 24 |
class TestReading:
|
| 25 |
+
@pytest.mark.parametrize("raw", ["true", "TRUE", " True ", "1", "on", "yes", "", " "]) # blank = default = ON
|
| 26 |
def test_true_spellings(self, monkeypatch, raw):
|
| 27 |
monkeypatch.setenv(MUZE_ENGINE_MAIN_TABS_VAR, raw)
|
| 28 |
assert muze_main_tabs_enabled() is True
|
| 29 |
|
| 30 |
+
@pytest.mark.parametrize("raw", ["false", "FALSE", "0", "off", "no"])
|
| 31 |
def test_false_spellings(self, monkeypatch, raw):
|
| 32 |
monkeypatch.setenv(MUZE_ENGINE_MAIN_TABS_VAR, raw)
|
| 33 |
assert muze_main_tabs_enabled() is False
|
| 34 |
|
| 35 |
+
def test_unset_is_on_and_says_so(self, monkeypatch):
|
| 36 |
+
"""Default ON since 2026-09-22: an unconfigured Space draws Gallery-grade
|
| 37 |
+
Muze on the main tabs; only an explicit false switches it off."""
|
| 38 |
monkeypatch.delenv(MUZE_ENGINE_MAIN_TABS_VAR, raising=False)
|
| 39 |
+
assert muze_main_tabs_enabled() is True
|
| 40 |
state = muze_main_tabs_state()
|
| 41 |
+
assert "ON" in state and "unset" in state
|
| 42 |
|
| 43 |
def test_a_set_value_names_itself(self, monkeypatch):
|
| 44 |
monkeypatch.setenv(MUZE_ENGINE_MAIN_TABS_VAR, "true")
|
|
|
|
| 86 |
return calls, changes
|
| 87 |
|
| 88 |
def test_off_skips_the_engine_and_stamps_off(self, monkeypatch):
|
| 89 |
+
monkeypatch.setenv(MUZE_ENGINE_MAIN_TABS_VAR, "false")
|
| 90 |
calls, changes = self._run(monkeypatch)
|
| 91 |
assert calls == []
|
| 92 |
assert any(c.startswith("Muze on main tabs: OFF") for c in changes), changes
|
|
|
|
| 99 |
|
| 100 |
def test_the_same_code_flips_with_the_environment(self, monkeypatch):
|
| 101 |
"""One commit, two Spaces: only the variable differs."""
|
| 102 |
+
monkeypatch.setenv(MUZE_ENGINE_MAIN_TABS_VAR, "false")
|
| 103 |
off, _ = self._run(monkeypatch)
|
| 104 |
monkeypatch.setenv(MUZE_ENGINE_MAIN_TABS_VAR, "true")
|
| 105 |
on, _ = self._run(monkeypatch)
|