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,187 @@
# -*- coding: utf-8 -*-
"""`tools/sweep_in_revit.py` obeys the DECLARED scope, and says what it left.
WHY THIS FILE IS SHAPED LIKE THIS
----------------------------------
The sweep script cannot be imported. It is written to be sent to a live Revit
by `rvt.py exec`, so it imports the Revit API at module scope and its last
line is `result = run(doc)` against a `doc` the bridge injects. Importing it
here would fail twice over.
So the source is read, cut at that last line, and the rest executed against
stubs for the three modules that genuinely need Revit -- `Autodesk.Revit.DB`,
`vibe.designspec` and `vibe.ticketqc`. `vibe.scope` is the REAL module: the
whole point is to prove that the declaration in the job file, and nothing
else, decides what this sweep measures.
What is still not covered: `measure()` and `run()`, which need a document.
Every mark below is a placeholder -- `PNL` for the pieces in scope, `PNLA`
for the near miss, `XX` for somebody else's. A real prefix is job
information and belongs in the job knowledge file, never here.
"""
import json
import re
import sys
import types
import pytest
from pathlib import Path
REPO = Path(__file__).resolve().parents[2]
SWEEP = REPO / 'tools' / 'sweep_in_revit.py'
SENTINEL = 'result = run(doc)'
# ------------------------------------------------------------- the harness
class FakeAssembly(object):
def __init__(self, name, eid):
self.Name = name
self._eid = eid
def _install(monkeypatch, assemblies_by_design):
"""Stub only what truly needs Revit, and put it back afterwards."""
revit_db = types.ModuleType('Autodesk.Revit.DB')
revit_db.ElementId = lambda value: value
revit = types.ModuleType('Autodesk.Revit')
revit.DB = revit_db
autodesk = types.ModuleType('Autodesk')
autodesk.Revit = revit
monkeypatch.setitem(sys.modules, 'Autodesk', autodesk)
monkeypatch.setitem(sys.modules, 'Autodesk.Revit', revit)
monkeypatch.setitem(sys.modules, 'Autodesk.Revit.DB', revit_db)
designspec = types.ModuleType('vibe.designspec')
designspec.sub_dir = lambda doc, name: str(doc)
ticketqc = types.ModuleType('vibe.ticketqc')
ticketqc.assemblies_by_design = lambda doc, know: assemblies_by_design
ticketqc.eid = lambda asm: asm._eid
ticketqc.collect_context = lambda doc: ({}, {})
vibe = sys.modules['vibe']
for name, module in (('designspec', designspec), ('ticketqc', ticketqc)):
monkeypatch.setitem(sys.modules, 'vibe.' + name, module)
monkeypatch.setattr(vibe, name, module, raising=False)
source = SWEEP.read_text(encoding='utf-8')
assert SENTINEL in source, 'the sweep no longer ends the way this reads it'
namespace = {'__name__': 'sweep_in_revit_under_test'}
exec(compile(source.split(SENTINEL)[0], str(SWEEP), 'exec'), namespace)
return namespace
@pytest.fixture
def sweep(monkeypatch):
def build(assemblies_by_design):
return _install(monkeypatch, assemblies_by_design)
return build
def _pieces():
return {'PNL1': [FakeAssembly('PNL101', 11), FakeAssembly('PNL102', 12)],
'PNLA1': [FakeAssembly('PNLA101', 21)],
'XX1': [FakeAssembly('XX101', 31), FakeAssembly('XX102', 32)]}
def _run_enumerate(sweep, know, tmp_path):
module = sweep(_pieces())
todo_path = str(tmp_path / 'todo.json')
scope_path = str(tmp_path / 'scope.json')
todo, info = module['enumerate_pieces'](object(), know, todo_path,
scope_path)
return module, todo, info, json.loads((tmp_path / 'scope.json')
.read_text(encoding='utf-8'))
# -------------------------------------------------- the declaration governs
def test_the_declaration_decides_what_is_measured(sweep, tmp_path):
_m, todo, _info, _saved = _run_enumerate(
sweep, {'scope': {'mark_prefixes': ['PNL']}}, tmp_path)
assert sorted(r['mark'] for r in todo) == ['PNL101', 'PNL102']
def test_changing_the_declaration_changes_the_sweep(sweep, tmp_path):
"""The hole this closes. The old code filtered on a literal, so editing
the job file moved nothing -- the declaration was decoration here while
every other consumer honoured it."""
_m, todo, _i, _s = _run_enumerate(
sweep, {'scope': {'mark_prefixes': ['XX']}}, tmp_path)
assert sorted(r['mark'] for r in todo) == ['XX101', 'XX102']
def test_no_declaration_sweeps_everything(sweep, tmp_path):
"""An undeclared scope is not an empty scope -- and a sweep that
measured nothing at all would look like a model with nothing in it."""
_m, todo, info, _s = _run_enumerate(sweep, {}, tmp_path)
assert len(todo) == 5
assert info['declared'] is False
assert 'No scope is declared' in info['line']
def test_a_mark_with_no_readable_prefix_is_swept_not_dropped(sweep, tmp_path):
module = sweep({'D1': [FakeAssembly('12345', 41)]})
todo, info = module['enumerate_pieces'](
object(), {'scope': {'mark_prefixes': ['PNL']}},
str(tmp_path / 'todo.json'), str(tmp_path / 'scope.json'))
assert [r['mark'] for r in todo] == ['12345']
assert 'no readable mark prefix' in info['line']
# ---------------------------------------------- and it says what it skipped
def test_what_was_left_alone_is_counted_named_and_written_beside_the_jobs(
sweep, tmp_path):
_m, _todo, info, saved = _run_enumerate(
sweep, {'scope': {'mark_prefixes': ['PNL']}}, tmp_path)
assert info['left_alone_count'] == 3
assert info['assemblies_seen'] == 5
assert sorted(info['left_alone']) == [('PNLA', 1), ('XX', 2)]
assert 'NOT JUDGED, OUT OF SCOPE' in info['line']
assert saved['line'] == info['line'] # the file, not just the return
# Named by kind on disk too, where a tuple has become a JSON list.
assert sorted(saved['left_alone']) == [['PNLA', 1], ['XX', 2]]
def test_the_near_miss_reaches_the_sweep_output(sweep, tmp_path):
_m, _todo, info, _saved = _run_enumerate(
sweep, {'scope': {'mark_prefixes': ['PNL']}}, tmp_path)
assert 'PNLA' in info['near_miss']
assert 'NEAR MISS' in info['headline']
def test_the_headline_fronts_every_return_value_because_the_caller_truncates(
sweep, tmp_path):
"""`tools/sweep.py` prints `str(out)[:200]`. The scope has to be in
those 200 characters or nobody reads it."""
module, _todo, info, _saved = _run_enumerate(
sweep, {'scope': {'mark_prefixes': ['PNL']}}, tmp_path)
headline = module['_scope_headline'](info)
assert headline.startswith('NOT JUDGED, OUT OF SCOPE')
assert len(headline) < 200
def test_a_sweep_folder_with_no_scope_file_says_so_rather_than_assuming(
sweep, tmp_path):
"""An older folder was enumerated by the literal. What it left out is
genuinely unknown, and inventing a reassuring line would be the silent
exclusion this whole mechanism refuses."""
module = sweep({})
said = module['_scope_headline'](None)
assert 'NOT RECORDED' in said
assert 're-run' in said
# --------------------------------------------------------- no job literals
def test_the_sweep_carries_no_hardcoded_mark_prefix():
"""The other half of the hole: a job's naming baked into a repository
that must not carry one. Catches `startswith('AB')` and its family."""
source = SWEEP.read_text(encoding='utf-8')
body = source.split('"""', 2)[-1] # past the module docstring
assert re.search(r"""startswith\(\s*['"][A-Za-z]""", body) is None
assert 'vibe import scope' in source or 'from vibe import' in source
assert 'scopelib.of(know)' in source