Attention-Tier Taxonomy Drift Across Workspace Regions¶
📜 Historical snapshot — not current docs
Captured 2026-04-20 during Dazzle's autonomous-improvement cycles. It records the framework as it was then and the gap being worked at the time; it may not describe current behaviour. Start from the documentation home, or see Project Evolution for how these fit together.
Date: 2026-04-20 (cycle 281) Class: Framework-level consistency gap Status: Open for implementation
Problem statement¶
Four workspace regions surface per-row attention signals (_attention envelope on each item) but each renders the signal with a different taxonomy + visual encoding:
| Region | Tiers supported | Visual encoding |
|---|---|---|
grid-region (UX-066) |
critical / warning / notice | 4px left border |
timeline-region (UX-067) |
critical / warning / fallback to default | SVG bullet marker colour (no notice tier) |
queue-region (UX-068) |
critical / warning / notice | 4px left border AND 0.04 alpha background tint |
list-region (UX-069) |
critical / warning / notice | 0.08 alpha background tint (critical/warning), 0.06 for notice (no border) |
The taxonomy drift surfaces three distinct classes of inconsistency:
-
Missing tier — timeline silently coalesces
noticeinto the default (no-attention) visual, so anotice-marked row in a timeline renders identically to a row with no attention at all. DSL authors who declarenoticeexpecting a subtle signal see nothing. -
Asymmetric encoding — grid uses border-only, list uses tint-only, queue uses both. The same semantic DSL declaration (
_attention: {level: critical}) produces visually divergent treatments depending on which region consumes the data. Users scanning a dashboard with both grid and list regions can't build a reliable mental model for what "critical" looks like. -
Asymmetric alpha values — list uses 0.08 (critical, warning) + 0.06 (notice); queue uses 0.04 uniform. Different visual intensity for the same semantic tier.
Evidence¶
- Cycle 275 (UX-066 grid-region contract) — documented 3-tier border-only taxonomy.
- Cycle 276 (UX-067 timeline-region contract) — documented missing
noticetier; v2 open question Q1 flagged it. - Cycle 277 (UX-068 queue-region contract) — documented unique dual-signal (border + tint).
- Cycle 278 (UX-069 list-region contract) — documented tint-only with 3-tier alpha variation.
- Cycle 278 log summary — explicitly called out the drift as "cross-region alignment candidate".
Additionally, prior implementations that informed this pattern:
- kanban-board (UX-040) — does NOT support per-row attention signals (cards are grouped by enum, no individual emphasis). Not an inconsistency; kanban's semantic is different.
- heatmap-region (UX-064) — uses a different signal (cell threshold colouring) that's not comparable to per-row attention.
Root cause hypothesis¶
The attention signal taxonomy was introduced organically as each region template was written, without a shared helper or macro. Each template's attention-rendering block is a hand-rolled Jinja {% if attn.level == 'critical' %}...{% elif... chain:
grid.html:15-18— border onlytimeline.html:13-15— bullet colour only (and only 2 tiers)queue.html:54-56— border + tintlist.html:78-80— tint only
The compiler produces the _attention envelope at src/dazzle_http/runtime/workspace_rendering.py (grep for _attention confirms shared production site), so the source-of-truth for the tier semantics IS canonical. The drift is entirely at the rendering layer.
This is a Heuristic 4 (defaults-propagation) class defect: canonical intent declared, canonical resolver correct, but rendering consumers each implemented their own branch logic without a shared macro.
Fix sketch¶
Option A (minimal): shared Jinja macro
Extract the tier-to-class mapping into a new macros/attention_accent.html file:
{% macro attention_classes(attn, style='border') %}
{% if attn %}
{% set level = attn.level %}
{% if style == 'border' %}
border-l-4
{% if level == 'critical' %}border-l-[hsl(var(--destructive))]{% endif %}
{% if level == 'warning' %}border-l-[hsl(var(--warning))]{% endif %}
{% if level == 'notice' %}border-l-[hsl(var(--primary))]{% endif %}
{% elif style == 'tint' %}
{% if level == 'critical' %}bg-[hsl(var(--destructive)/0.08)]{% endif %}
{% if level == 'warning' %}bg-[hsl(var(--warning)/0.08)]{% endif %}
{% if level == 'notice' %}bg-[hsl(var(--primary)/0.06)]{% endif %}
{% elif style == 'both' %}
border-l-4
{% if level == 'critical' %}border-l-[hsl(var(--destructive))] bg-[hsl(var(--destructive)/0.04)]{% endif %}
{% if level == 'warning' %}border-l-[hsl(var(--warning))] bg-[hsl(var(--warning)/0.04)]{% endif %}
{% if level == 'notice' %}border-l-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.04)]{% endif %}
{% elif style == 'bullet' %}
{% if level == 'critical' %}text-[hsl(var(--destructive))]{% endif %}
{% if level == 'warning' %}text-[hsl(var(--warning))]{% endif %}
{% if level == 'notice' %}text-[hsl(var(--primary))]{% endif %}
{% if not level %}text-[hsl(var(--primary))]{% endif %}
{% endif %}
{% endif %}
{% endmacro %}
Each region then imports + uses the macro with its preferred style variant:
- grid: {{ attention_classes(attn, 'border') }}
- timeline: {{ attention_classes(attn, 'bullet') }}
- queue: {{ attention_classes(attn, 'both') }}
- list: {{ attention_classes(attn, 'tint') }}
Alpha values kept per-style (list 0.08/0.06, queue 0.04) because visual density differs between regions. The macro pins the semantic token mapping; individual style variants keep their visual-intensity choice.
Option B (consolidation): align tint alphas While touching the macro, decide whether all 3 bg-tint variants (list 0.08/0.06, queue 0.04) should use the same alpha. Arguments: - For alignment: predictable mental model across regions. - Against: queue has border AND tint (1+1 signals), list has tint alone (1 signal). Alpha of queue's tint can be lower because the border carries half the signal.
Recommendation: do Option A first (extract macro). Leave alphas as-is. Revisit Option B in a follow-up cycle after the macro lands and any consumer hasn't regressed.
Option C (bigger): promote timeline to 3-tier
Fix timeline's missing notice tier by updating the template's {% else %} branch to explicitly check for level == 'notice' before falling through to default:
{% if attn and attn.level == 'critical' %}text-[hsl(var(--destructive))]
{% elif attn and attn.level == 'warning' %}text-[hsl(var(--warning))]
{% elif attn and attn.level == 'notice' %}text-[hsl(var(--primary))]
{% else %}text-[hsl(var(--primary))]{% endif %}
Note: if default is also --primary, the fix is cosmetic (both render the same colour). Currently timeline's default IS --primary, so notice → default is a no-visible-difference collapse. But promoting notice to its own branch makes the taxonomy explicit + allows future divergence (e.g. default → muted grey, notice → primary blue).
Blast radius¶
- Affected regions: grid (UX-066), timeline (UX-067), queue (UX-068), list (UX-069)
- Affected apps: all 5 example apps that use any of these regions — and every downstream Dazzle app that consumes
display: grid|timeline|queue|list(default mode, so effectively every app). - Regression tests needed: each of the 4 region contracts already has attention-level tests pinning the current behaviour. Changing the behaviour requires updating assertions. Cross-persona:
_attentionenvelope is built server-side from access-evaluator results; persona-gated data already drives the envelope correctly. - Visual regression risk: if alphas are aligned in Option B, existing dashboards will see a subtle visual shift. Users may not notice (it's already inconsistent); designers may care.
Open questions¶
-
Is
noticeactually used anywhere? A grep of the workspace_rendering.py + example DSLs showsnoticeonly in test fixtures. In production,_attentionvalues flow from entity-specific logic (often based on criticality thresholds). If no real consumer usesnotice, the "missing tier" finding in timeline is academic. -
Should attention tiers be DSL-configurable? Currently the framework emits
critical | warning | notice. DSL authors could want domain-specific tiers (blocked | stalled | latefor a project app). Out of scope for this gap doc; worth a dedicated DSL-design cycle. -
Tint alpha normalisation — should the macro standardise on one value (say 0.06) across all variants? Needs designer input. Currently undocumented but consistent with "queue has 2 signals, list has 1, list's signal is stronger".
-
Does
kanban-board(UX-040) benefit from attention signals? Currently kanban doesn't support them. But a critical-priority ticket in a kanban column could benefit from a left-border accent. Out of scope for the initial macro extraction; could be a v2 kanban enhancement. -
Accessibility impact — attention signal is colour-encoded only. Colour-vision-deficient users lose the signal. A future cycle could add an
aria-describedbyreferencing theattn.messagefield, but the macro extraction doesn't need to wait for that.
Implementation sketch¶
Order of operations:
1. Write src/dazzle_page/templates/macros/attention_accent.html with the 4 style variants.
2. Migrate grid.html:15-18 → attention_classes(attn, 'border').
3. Migrate timeline.html:13-15 → attention_classes(attn, 'bullet').
4. Migrate queue.html:54-56 → attention_classes(attn, 'both').
5. Migrate list.html:78-80 → attention_classes(attn, 'tint').
6. Update each region's regression tests to use the shared mapping (assertions stay the same since the rendered class strings stay identical).
7. Add a new TestAttentionAccentMacro class covering all 4 style variants × 3 tiers × presence-of-attn axis.
8. Per Heuristic 3: verify on all 5 example apps before ship — check a representative dashboard still renders attention rows correctly.
Estimated scope: one /ux-cycle session, ~60-90 min.
Not in scope: Option B (alpha normalisation) or Option C (timeline's notice tier promotion). Those are follow-up cycles; this gap doc is specifically about the extraction.