feat(#84): IMP-84 u1~u3 silent automation policy enforcement (FramePanel reject confirm + slide_base provisional badge/outline + IMP-30 visual assertions inverted)
Some checks failed
Multi-MDX Regression (IMP-91) / multi-mdx-regression (push) Failing after 21s
Some checks failed
Multi-MDX Regression (IMP-91) / multi-mdx-regression (push) Failing after 21s
- u1 FramePanel.tsx: extract `applyFrameSelection(candidate, onFrameSelect)` pure helper; collapse `handleFrameSelect` to direct onFrameSelect for every V4 label; drop `window.confirm` reject popup (IMP-47B u11 regression noise per `feedback_auto_pipeline_first`). New vitest pin `imp84_framepanel_reject_silent.test.ts` covers helper invocation across all 4 V4 labels + source-presence pins. - u2 templates/phase_z2/slide_base.html: delete `.zone--provisional` CSS, `.zone__needs-adaptation-badge` CSS, the zone--provisional class fragment in the zone div, and the badge `<span>` render at the provisional zone. Preserve `data-provisional="1"` attribute as silent telemetry. New pytest `tests/phase_z2/test_imp84_provisional_silent_render.py` pins the silent contract independently of the IMP-30 first-render file. - u3 tests/test_phase_z2_imp30_first_render.py: invert the three IMP-30 u5 positive provisional-visual assertions to IMP-84 silent-contract negatives (no class, no badge, no CSS selectors); preserve positive `data-provisional` telemetry assertions. Docstrings updated to IMP-84 silent contract. Out of scope (Round #4 + #92 contract): Home.tsx `toast.error(aiReviewMsg)` call line, designAgentApi.ts `api_error_kinds`/`api_error_kind` schema and operational-only formatter, FramePanel reject badge/tooltip read-only labels (L102/L147/L156), and backend `zone.provisional` flag emission. Stage 4 PASS: u1 vitest 10/10, u2 pytest 5/5, u3 pytest 29/29 (incl. 3 IMP-84 inverted assertions: `test_imp84_provisional_zone_silent_no_class_no_badge`, `test_imp84_provisional_badge_never_rendered_in_mixed_zones`, `test_imp84_slide_base_css_strips_provisional_visual_selectors`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -676,12 +676,20 @@ def test_u5_zone_without_provisional_key_treated_as_non_provisional():
|
||||
# ─── u5 case 2 : provisional zone renders class + badge + data attr ───
|
||||
|
||||
|
||||
def test_u5_provisional_zone_renders_class_and_badge():
|
||||
"""Opt-in path. zones[i].provisional=True must:
|
||||
1. Append `zone--provisional` class to the zone div.
|
||||
2. Set `data-provisional="1"` data attribute (for downstream selectors).
|
||||
3. Render a `<span class="zone__needs-adaptation-badge">` element with
|
||||
the literal text "needs adaptation" (aria-label included for a11y).
|
||||
def test_imp84_provisional_zone_silent_no_class_no_badge():
|
||||
"""IMP-84 silent-automation inversion of the prior IMP-30 u5 contract.
|
||||
Under the silent contract, zones[i].provisional=True must:
|
||||
1. NOT append `zone--provisional` class to the zone div (no user-visible
|
||||
outline / striped wash).
|
||||
2. Still set `data-provisional="1"` data attribute as silent telemetry
|
||||
for downstream selectors / inspection.
|
||||
3. NOT render any `<span class="zone__needs-adaptation-badge">` element
|
||||
and NOT surface the literal text "needs adaptation" or its
|
||||
aria-label (no user-facing badge).
|
||||
|
||||
Scope: assertions target the zone div body. The CSS <style> block must
|
||||
likewise not carry the removed visual selectors — that surface is pinned
|
||||
in `test_imp84_slide_base_css_strips_provisional_visual_selectors` below.
|
||||
"""
|
||||
zones = [
|
||||
{
|
||||
@@ -694,21 +702,26 @@ def test_u5_provisional_zone_renders_class_and_badge():
|
||||
}
|
||||
]
|
||||
html = _render_slide_base(zones)
|
||||
# zone--provisional class must appear on the zone div for position=single.
|
||||
assert "zone--provisional" in html
|
||||
# data-provisional="1" attribute must be present.
|
||||
assert 'data-provisional="1"' in html
|
||||
# Badge element with the required label text.
|
||||
assert 'class="zone__needs-adaptation-badge"' in html
|
||||
assert "needs adaptation" in html
|
||||
assert 'aria-label="needs user or AI adaptation"' in html
|
||||
zone_divs = _all_zone_div_openings(html)
|
||||
assert len(zone_divs) == 1
|
||||
# No zone--provisional class on the zone div (visual removed).
|
||||
assert "zone--provisional" not in zone_divs[0]
|
||||
# data-provisional="1" attribute still present as silent telemetry.
|
||||
assert 'data-provisional="1"' in zone_divs[0]
|
||||
# No badge <span> element and no badge label text anywhere in the body.
|
||||
assert _all_badge_spans(html) == []
|
||||
assert "needs adaptation" not in html
|
||||
assert 'aria-label="needs user or AI adaptation"' not in html
|
||||
|
||||
|
||||
def test_u5_provisional_badge_appears_inside_provisional_zone_only():
|
||||
"""Mixed-zone slide: one provisional zone + one normal zone. The badge
|
||||
+ class must appear ONLY in the provisional zone, not bleed into the
|
||||
normal one (CSS-level isolation should already prevent this, but the
|
||||
template must not emit the badge for both)."""
|
||||
def test_imp84_provisional_badge_never_rendered_in_mixed_zones():
|
||||
"""IMP-84 silent-automation inversion of the prior IMP-30 u5 mixed-zone
|
||||
contract. Mixed-zone slide: one provisional zone + one normal zone. The
|
||||
silent contract requires that NO badge span and NO `zone--provisional`
|
||||
class be emitted on either zone div. The provisional zone is identifiable
|
||||
only through the silent `data-provisional="1"` telemetry attribute, which
|
||||
must be scoped to the provisional zone alone (no bleed onto the normal
|
||||
zone)."""
|
||||
zones = [
|
||||
{
|
||||
"position": "top",
|
||||
@@ -735,21 +748,21 @@ def test_u5_provisional_badge_appears_inside_provisional_zone_only():
|
||||
html = _render_slide_base(
|
||||
zones, layout_preset="vertical-2", layout_css=layout_css
|
||||
)
|
||||
# Exactly one badge span element should be present in the rendered body
|
||||
# (CSS selector in <style> excluded by the helper).
|
||||
assert len(_all_badge_spans(html)) == 1
|
||||
# zone--provisional must appear on exactly one zone div (CSS selector
|
||||
# in <style> excluded by the helper).
|
||||
# No badge <span> element should be rendered anywhere in the body
|
||||
# (silent-automation policy).
|
||||
assert _all_badge_spans(html) == []
|
||||
# No zone div should carry the zone--provisional class (visual removed).
|
||||
zone_divs = _all_zone_div_openings(html)
|
||||
assert len(zone_divs) == 2
|
||||
provisional_zone_divs = [d for d in zone_divs if "zone--provisional" in d]
|
||||
assert len(provisional_zone_divs) == 1
|
||||
# The provisional class must be associated with the bottom zone.
|
||||
assert all("zone--provisional" not in d for d in zone_divs)
|
||||
# data-provisional="1" telemetry must be present on the bottom (provisional)
|
||||
# zone only — never on the top (non-provisional) zone.
|
||||
bottom_zone_open = _zone_div_for_position(html, "bottom")
|
||||
assert "zone--provisional" in bottom_zone_open
|
||||
assert "zone__needs-adaptation-badge" in bottom_zone_open
|
||||
# The top zone must NOT carry the provisional class.
|
||||
assert 'data-provisional="1"' in bottom_zone_open
|
||||
assert "zone--provisional" not in bottom_zone_open
|
||||
assert "zone__needs-adaptation-badge" not in bottom_zone_open
|
||||
top_zone_open = _zone_div_for_position(html, "top")
|
||||
assert 'data-provisional="1"' not in top_zone_open
|
||||
assert "zone--provisional" not in top_zone_open
|
||||
assert "zone__needs-adaptation-badge" not in top_zone_open
|
||||
|
||||
@@ -779,15 +792,19 @@ def test_u5_zones_data_provisional_field_defaults_false_in_template():
|
||||
assert _all_badge_spans(html) == []
|
||||
|
||||
|
||||
def test_u5_slide_base_css_carries_provisional_marker_styles():
|
||||
"""The provisional visual contract (dashed outline + striped wash + badge)
|
||||
is defined in slide_base.html <style>. Pin that the relevant CSS class
|
||||
selectors exist in the rendered HTML so a refactor that removes them
|
||||
breaks this test rather than silently rendering an unstyled badge.
|
||||
def test_imp84_slide_base_css_strips_provisional_visual_selectors():
|
||||
"""IMP-84 silent-automation inversion of the prior IMP-30 u5 CSS-presence
|
||||
contract. The provisional visual treatment (dashed outline + striped wash
|
||||
+ badge) was deleted from `slide_base.html <style>` by IMP-84 u2. Pin
|
||||
that the CSS class selectors `.zone--provisional` and
|
||||
`.zone__needs-adaptation-badge` no longer appear in the rendered HTML —
|
||||
a refactor that re-introduces them must break this test rather than
|
||||
silently re-surfacing the removed visual signal.
|
||||
|
||||
This is a class-selector existence check; it does not validate the
|
||||
specific color / dash pattern, which is a design decision intentionally
|
||||
left malleable (e.g., palette swap for a different theme)."""
|
||||
Scope: the assertion targets the entire rendered HTML (style block plus
|
||||
body). Since the body badge span is also gone (covered separately above),
|
||||
any occurrence of these strings in the rendered output would only come
|
||||
from a regressed style block."""
|
||||
zones = [
|
||||
{
|
||||
"position": "single",
|
||||
@@ -799,9 +816,9 @@ def test_u5_slide_base_css_carries_provisional_marker_styles():
|
||||
}
|
||||
]
|
||||
html = _render_slide_base(zones)
|
||||
# Style block must define .zone--provisional and the badge selector.
|
||||
assert ".zone--provisional" in html
|
||||
assert ".zone__needs-adaptation-badge" in html
|
||||
# Style block must NOT define .zone--provisional or the badge selector.
|
||||
assert ".zone--provisional" not in html
|
||||
assert ".zone__needs-adaptation-badge" not in html
|
||||
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user