# -*- coding: utf-8 -*-
"""Which end of the piece is up, when it came from a fallback -- and the red
line telling the reader to go check TOT on the model.
`report.up_end_disclosure()` / `report._up_end_block()` already print the
fallback's own explanation, unconditionally, whenever `source.up_end_basis`
is anything but the ruled mechanism (`RULED_UP_END_BASIS`). What is new here
is `report._up_end_is_origin_defect()`: of the three reasons the ruled
mechanism can decline (see `up_end_from_origin()` in
`WillTools.extension/lib/vibe/knowledge.py`), only the FIRST -- the origin's
own up vector does not point up -- is a defect on the assembly's origin
itself. The other two (near-square; not yet confirmed for this piece's
orientation) are gaps in what the rule covers, not something wrong with the
model, and must never carry the same red call to action.
"""
import re
from conftest import make_panel
from panellift import report
# Verbatim from `up_end_from_origin()`'s FIRST refusal
# (WillTools.extension/lib/vibe/knowledge.py) -- the exact shape
# `liftexport` writes into `source.up_end_basis_why`, and the real text on
# WP401 (2026-09-01).
ORIGIN_NOT_UP_WHY = (
"The origin of assembly WP401 does not stand the piece up: its up "
"basis is (0.0, 1.0, 0.0) rather than world up. One assembly in 282 is "
"like this -- its face normal points up instead -- and on such a "
"piece every reading off the frame is wrong, so nothing is read off "
"this one. Which end is up was therefore read from GLOBAL ELEVATION "
"instead -- the end of the piece that is higher in the model. That is "
"a fallback, not the rule.")
# Verbatim shape of the SECOND refusal (near square) -- must NOT flag.
NEAR_SQUARE_WHY = (
"Assembly PNL127 is near square (11.63 x 11.58 ft, ratio 1.00), and "
"below 1.25 the long direction cannot be told from the up direction.")
# Verbatim shape of the THIRD refusal (not yet confirmed for this
# orientation) -- must NOT flag either; this is a coverage gap, not a
# defect on the model. Matches `up_end_from_origin()`'s wording as fixed
# 2026-09-01: green (BasisY/world-up) is now confirmed for a vertical wall
# panel too, so the message no longer claims nobody has said how vertical
# panels work -- only that this function has not been extended to run
# there, which is a coverage gap and NOT a defect on the origin.
NOT_HORIZONTAL_WHY = (
"The origin rule is confirmed for HORIZONTAL panels only, and the "
"origin of assembly PNL1 stands its length up, which is not one. The "
"owner has confirmed the same green-arrow reading also holds on a "
"vertical wall panel (2026-09-01), but this rule has not been "
"extended to run there yet, so the origin is not read on this piece.")
def _panel_with_fallback(why, basis='global elevation'):
panel = make_panel()
panel.source = {
'up_end_basis': basis,
'up_end_basis_why': why,
'up_end_affects': 'erection -- which row of face lifters the crane '
'cannot reach, and which edge the anchors are on',
}
return panel
# ------------------------------------------------- _up_end_is_origin_defect
def test_the_origin_not_up_refusal_is_flagged():
assert report._up_end_is_origin_defect(ORIGIN_NOT_UP_WHY) is True
def test_the_near_square_refusal_is_not_flagged():
assert report._up_end_is_origin_defect(NEAR_SQUARE_WHY) is False
def test_the_not_yet_confirmed_orientation_refusal_is_not_flagged():
assert report._up_end_is_origin_defect(NOT_HORIZONTAL_WHY) is False
def test_empty_or_none_why_is_not_flagged():
assert report._up_end_is_origin_defect('') is False
assert report._up_end_is_origin_defect(None) is False
# --------------------------------------------------------- _up_end_block()
def test_the_red_line_appears_when_the_origin_itself_is_wrong():
"""WP401's real shape: the origin does not point up at all, so this is
a defect on the assembly's own TOT, not a caveat about the analysis."""
panel = _panel_with_fallback(ORIGIN_NOT_UP_WHY)
block = report._up_end_block(panel)
assert 'tot-flag' in block
assert 'TOT' in block
# The short fact is always visible; the measured reasoning ("does not
# stand the piece up") is inside the fold, not repeated at top level.
assert 'read from global elevation, not the assembly origin' in block
assert 'does not stand the piece up' in block
def test_the_block_is_short_a_few_lines_not_a_wall_of_text():
"""The owner's own words, 2026-09-01: '把这一段从lift的结果里面删掉,
或者缩短...太长了压根不会看的' -- delete this section or shorten it, it
is so long nobody will read it at all. The block he cut was ~700
characters; the always-visible text (outside the fold) must be a small
fraction of that."""
panel = _panel_with_fallback(ORIGIN_NOT_UP_WHY)
block = report._up_end_block(panel)
# Everything inside the fold is closed by default and does not count
# against "what a reader sees at a glance" -- strip it out before
# measuring, the same way a browser would hide it on load. Tags do not
# count either: they are never visible text, only the characters
# between them are.
fold_start = block.index('') + len(' ')
always_visible = block[:fold_start] + block[fold_end:]
visible_text = re.sub(r'<[^>]+>', '', always_visible)
assert len(visible_text) < 250, (len(visible_text), visible_text)
def test_fallback_not_the_rule_appears_at_most_once():
"""The old block said this exact clause TWICE in one paragraph -- once
as the report's own intro sentence, once again from the end of the
producer string it quoted. That repetition, not the length alone, was
what the owner named as the reason he stopped reading."""
panel = _panel_with_fallback(ORIGIN_NOT_UP_WHY)
block = report._up_end_block(panel)
assert block.count('fallback, not the rule') <= 1
def test_the_full_reasoning_is_folded_using_the_report_s_own_mechanism():
"""Not deleted, not moved to a different wall of visible text -- folded
behind the SAME /_sec() idiom every other section already
uses, closed by default."""
panel = _panel_with_fallback(ORIGIN_NOT_UP_WHY)
block = report._up_end_block(panel)
assert '' in block
assert 'Why
' in block
assert 'open' not in block.split('')[0]
# The full producer reasoning is still there -- just inside the fold.
assert 'One assembly in 282' in block
assert 'That is a fallback, not the rule.' in block
def test_the_red_line_is_absent_for_the_near_square_refusal():
"""A near-square piece is genuinely undecidable, not a broken origin --
the red 'go check TOT' line would send the reader to fix a model that
is not wrong."""
panel = _panel_with_fallback(NEAR_SQUARE_WHY)
block = report._up_end_block(panel)
assert block # the disclosure itself still prints
assert 'tot-flag' not in block
assert 'TOT' not in block
def test_the_red_line_is_absent_for_the_not_yet_confirmed_orientation():
"""A vertical panel the rule has not been extended to cover yet is a
known gap, not a defect -- same non-flag as the near-square case."""
panel = _panel_with_fallback(NOT_HORIZONTAL_WHY)
block = report._up_end_block(panel)
assert block
assert 'tot-flag' not in block
assert 'TOT' not in block
def test_no_block_at_all_when_the_ruled_mechanism_answered():
"""`up_end_basis` == the ruled mechanism (`RULED_UP_END_BASIS`) means
there is nothing to disclose -- and therefore nothing to flag."""
panel = _panel_with_fallback('assembly origin should never get here',
basis=report.RULED_UP_END_BASIS)
assert report._up_end_block(panel) == ''
def test_no_block_at_all_when_the_panel_has_no_source_disclosure():
panel = make_panel()
panel.source = {}
assert report._up_end_block(panel) == ''
def test_the_flag_uses_the_same_red_every_fail_on_the_page_already_uses():
"""Match the report's existing severity styling rather than inventing a
new colour -- var(--fail), the same variable `.FAIL` uses."""
assert '.tot-flag{color:var(--fail)' in report.CSS
def test_the_flag_is_small_text_not_a_banner():
"""It sits inside the existing warn-styled .upend box; it must not carry
its own background, border or oversized font that would read as a
second banner on the page."""
css = report.CSS
start = css.index('.tot-flag{')
end = css.index('}', start)
rule = css[start:end]
assert 'font-size:12px' in rule
assert 'background' not in rule
assert 'border' not in rule