WillTools 2026-09-09 -- clean snapshot of the development repo for review
ci / test (push) Successful in 6m14s

History-free snapshot of the private development repository at 77a82eb.
Code, docs and the public catalogue only: no plant or job knowledge
layers, no client, job or producer names (tools/scrub.py clean against
the widened term list; a second word-level grep clean). The intro deck is
left out because its slide images cannot be checked for job drawings.
This commit is contained in:
2026-09-09 08:35:15 -05:00
commit 25592fa6c2
406 changed files with 223435 additions and 0 deletions
@@ -0,0 +1,197 @@
# -*- coding: utf-8 -*-
"""The handling stress limit, from the window down to the job file.
`handling.tension_limit_sqrt_fc` multiplies sqrt(f'c) to give the tensile
allowable in psi, and there are two defensible answers: 5.0 -- the modulus of
rupture over a 1.5 safety factor, "no discernible cracking" -- and 7.5, the
rupture modulus itself, first cracking with no margin. They are a factor of
1.5 apart on EVERY stress verdict, which is the largest single source of
disagreement between this tool and a commercial check of the same panel.
The owner would not pick one for everybody, 2026-08-30:
"应力极限写入 projectconfig 里面的 lifting&handling 板块里面去,让用户自己
填写。I can see it both ways."
So it moved OUT of `configform.NOT_SHOWN` and INTO the lifting & handling
module as a number box. What is tested here is the half that makes that
honest: the key a person types into is the key `liftexport` reads, and the
value reaches every stage of the job file panellift is handed. A field that
writes a key nothing reads is the drift `tools/knowledge_drift.py` exists to
catch, and it has caught it before.
"""
from vibe import configform, configmodel, knowledge, liftexport
from test_liftexport import FACE_AND_EDGE_LIFTERS, make_job, make_know
KEY = 'handling.tension_limit_sqrt_fc'
# ------------------------------------------------------- it is in the window
def test_the_stress_limit_is_a_field_in_lifting_and_handling():
"""The owner's ruling, as a placement. It used to be in NOT_SHOWN with
the reason 'a stress limit is argued for once and then left alone' --
which was an argument for having a doctrine, and he declined to have
one."""
assert configform.MODULE_OF[KEY] == configform.LIFTING
assert KEY not in configform.NOT_SHOWN
def test_it_is_a_box_you_type_a_number_into():
field = [f for f in configform.settings_fields() if f['key'] == KEY][0]
assert field['kind'] == 'number'
ok, value, _why = configform.parse(field, '7.5')
assert ok and value == 7.5
def test_the_row_note_puts_both_reference_values_in_front_of_the_typist():
"""A box that says 'stress limit' and nothing else asks a person to
remember which two numbers are argued about. The whole reason this is a
field rather than a doctrine is that the choice is the engineer's, so
the window has to show what is being chosen between."""
field = [f for f in configform.settings_fields() if f['key'] == KEY][0]
note = field['note']
assert '5.0' in note and '7.5' in note
assert "sqrt(f'c)" in note # what the number multiplies
assert 'psi' in note
assert 'cracking' in note # and what each end of it means
def test_the_full_view_says_where_it_is_edited_now():
"""`configpage` reads `placement()`, so the two halves of the answer
come from one table. Before the move this key was absent from it and
the page said 'the file'."""
assert configform.placement()[KEY] == 'Lifting and handling'
def test_nothing_fell_through_the_move():
"""The guard that makes the not-shown pool honest: every editable
setting is in a module or has a written reason for not being."""
assert configform.unmapped() == []
assert len(configform.not_shown_rows()) >= 8
assert KEY not in [key for _label, key, _why
in configform.not_shown_rows()]
# ------------------------------------------------------------ what it refuses
def test_a_zero_or_negative_limit_is_refused():
"""The only refusal, and it is not an opinion: a zero allowable fails
every piece at any stress, and a negative one inverts the comparison so
that every piece passes."""
assert configmodel.validate(KEY, 0)
assert configmodel.validate(KEY, -5.0)
assert 'greater than zero' in ' '.join(configmodel.validate(KEY, 0))
def test_both_schools_are_accepted_without_argument():
"""5.0 and 7.5 are each somebody's defensible position. The validator
does not get to have a doctrine the owner declined to have."""
assert configmodel.validate(KEY, 5.0) == []
assert configmodel.validate(KEY, 7.5) == []
def test_the_extremes_are_warned_about_and_still_allowed():
"""Above 7.5 and below 3 are worth saying something about; neither is
refused, because a refusal here would be the tool taking the position
the owner left to the engineer."""
assert configmodel.validate(KEY, 9.0) == []
assert configmodel.validate(KEY, 2.0) == []
high = ' '.join(configmodel.warnings(KEY, 9.0))
low = ' '.join(configmodel.warnings(KEY, 2.0))
assert 'rupture' in high and high
assert 'conservative' in low and low
def test_seven_point_five_says_what_it_is():
said = ' '.join(configmodel.warnings(KEY, 7.5))
assert 'first cracking' in said and 'no margin' in said
def test_the_ordinary_values_say_nothing():
"""A warning on every value is a warning nobody reads."""
assert configmodel.warnings(KEY, 5.0) == []
assert configmodel.warnings(KEY, 6.0) == []
assert configmodel.warnings(KEY, 3.5) == []
# ------------------------------------------------- it reaches the calculation
def test_the_default_is_still_five():
"""Byte-identity when nobody sets it. Every job configured today has no
override, and this is the number they have all been running."""
assert knowledge.get(knowledge.DEFAULTS, KEY) == 5.0
def test_the_job_layer_value_lands_on_every_stage_of_the_job_file():
"""The chain, as one assertion: knowledge -> default_stages -> the
stages panellift is handed. This is the link that makes the field real
rather than a knob nothing turns."""
know = make_know()
know['handling']['tension_limit_sqrt_fc'] = 7.5
stages, _rows, _problems = liftexport.default_stages(
make_job(FACE_AND_EDGE_LIFTERS), know)
assert stages
assert all(s['tension_limit_sqrt_fc'] == 7.5 for s in stages)
def test_an_unset_limit_writes_the_five_it_always_wrote():
stages, _rows, _problems = liftexport.default_stages(
make_job(FACE_AND_EDGE_LIFTERS), make_know())
assert all(s['tension_limit_sqrt_fc'] == 5.0 for s in stages)
assert all(s['tension_limit_source'] == 'job' for s in stages)
def test_a_stage_that_states_its_own_limit_is_marked_as_the_override():
"""A `handling.stages` entry beats the job-wide field. The engineer who
typed 5.0 into Project Config and reads 7.5 on the report has to be able
to see why on the page rather than by opening the YAML."""
know = make_know()
know['handling']['tension_limit_sqrt_fc'] = 5.0
know['handling']['stages'][0]['tension_limit_sqrt_fc'] = 7.5
stages, _rows, _problems = liftexport.default_stages(
make_job(FACE_AND_EDGE_LIFTERS), know)
assert stages[0]['tension_limit_sqrt_fc'] == 7.5
assert stages[0]['tension_limit_source'] == 'stage'
assert stages[1]['tension_limit_sqrt_fc'] == 5.0
assert stages[1]['tension_limit_source'] == 'job'
def test_the_provenance_words_are_the_ones_panellift_reads():
"""Written on one side, refused on the other if they disagree. panellift
raises on an unknown word rather than printing it, so the two lists have
to be the same list."""
stages, _rows, _problems = liftexport.default_stages(
make_job(FACE_AND_EDGE_LIFTERS), make_know())
assert set(s['tension_limit_source'] for s in stages) <= \
set(['job', 'stage', 'default'])
# ------------------------------------------------- and the warning is spoken
def _button_source():
import io
from test_configform import SCRIPT
return io.open(SCRIPT, encoding='utf-8').read()
def test_the_window_actually_calls_warnings():
"""`configmodel.warnings()` had no caller outside the test suite -- every
sentence in it was written and never shown. This setting is the reason it
needs one: 5.0 and 7.5 are both defensible so neither may be refused, and
the only thing left to do is say what the number means before it is
written."""
source = _button_source()
assert 'configmodel.warnings(' in source
def test_a_warning_is_not_dressed_as_a_refusal():
"""The two read the same on screen and mean opposite things. The window
paints the line so it gets read and words it so it is not mistaken for a
rejection, and it survives the window closing into the summary."""
source = _button_source()
assert 'ALLOWED, WITH A NOTE' in source
assert 'Allowed, with a note' in source
assert 'self.cautioned' in source