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,206 @@
# -*- coding: utf-8 -*-
"""PLANT EXTRACTION RULE PACKS: one producer's drawing habits, kept out of
the generic reader and loaded only where the plant knowledge says so.
WHY THIS EXISTS
---------------
`ticketqc.parse_design_summary` reads a design summary with rules that are
true of precast drawings generally -- an f'c line, a strand callout, a mesh
callout. Every text note it cannot place lands in the spec's `unresolved`
list verbatim, which is what stops a narrow rule set from being a lossy one.
But a real sheet carries a second population of notes that are not general at
all. They are one producer's house style: the title of a standard detail, the
label above a legend, a standing instruction that appears on every sheet that
producer draws. The generic reader cannot place them and should not try --
they are not facts about precast, they are facts about an office.
Left alone they are not harmless. Seven of them on one sheet turn `unresolved`
into a list nobody reads, and the day a genuinely unread value lands there it
is the eighth line of a list the reader has already learned to skim. A signal
that always fires is not a signal -- the same argument `.setup-opts` makes for
a check that always FAILs, and the same one `checks.muted` makes for a row
that is permanently NOT RUN.
WHAT A PACK MAY DO, AND WHAT IT MAY NEVER DO
--------------------------------------------
A pack classifies a note the generic rules did not match. It may say:
* `note` -- prose that belongs in the spec's `notes` list. An
instruction, a condition, a standing rule. It is CARRIED,
verbatim, into a field a person reads.
* `furniture` -- drawing furniture that carries no data at all: the bare
title of a legend or a detail. It STAYS in `unresolved`,
with the pack's reason in place of the blanket one.
A pack may NEVER remove a note from the spec, and there is no third verdict
that makes one disappear. `furniture` is not an ignore list: the line is still
written out, still readable, still countable -- only the sentence beside it
changes, from "no rule matched this" to "this is a legend title, it carries no
data". An exclusion the code chose for itself is the one failure this project
has already paid for (see `vibe/scope.py`), and nothing here may repeat it.
WHEN UNSURE, A PACK SAYS `note`, NEVER `furniture`
--------------------------------------------------
The two errors are not symmetric, in the same way `vibe/scope.py`'s two are
not. A note wrongly called `note` is copied into a field a person reads and
costs them one line of attention. A note wrongly called `furniture` has been
declared meaningless by a regex, and the reader is told there is nothing
there. So a rule that is not certain writes `note`, and a title that carries
any condition beyond its own name (`STRIPPING & BLOCKING DETAIL < 25'`) is a
note, not furniture -- the condition is the part that could matter.
WHICH PACKS LOAD IS DECLARED, NEVER INFERRED
--------------------------------------------
From the PLANT layer, `extract.rule_packs`. Not from `plant.id`, even though
that key exists and would be enough to guess with: which house style a job is
drawn in is a thing somebody knows and can write down, and a guess that is
right today is a guess that goes wrong silently the first time a job is drawn
to another producer's standard on this plant's template.
extract:
rule_packs: [mc]
Undeclared means NO PACK LOADS and every note behaves exactly as it did
before this module existed -- unmatched notes land in `unresolved` under the
generic reason. That is the honest default: a machine that has been told
nothing about an office's habits should claim to recognise nothing.
A declared name this module does not know is REFUSED, loudly, and no pack
loads at all. Same discipline as `tools/setup_machine.py`'s `.setup-opts`
check: an opt file that has gone quiet about a line nobody honours is worse
than no opt file, because the reader believes something is in force that is
not. `problems` comes back beside the packs and the caller prints it.
Nothing here imports a module named in knowledge. `PACKS` is a whitelist of
names this package ships; a name outside it is an error, never an import.
IronPython 2.7: no f-strings, no pathlib, no typing, no dataclasses.
"""
# Declared name -> module basename inside this package. A WHITELIST: the
# knowledge layer chooses among these, it never names a module to import.
PACKS = {
'mc': 'mc',
}
KEY = 'extract.rule_packs'
# What a pack may return from `classify`. `note` is carried into the spec's
# notes; `furniture` stays in unresolved with a better reason. There is no
# verdict that drops a line -- see the module docstring.
NOTE = 'note'
FURNITURE = 'furniture'
KINDS = (NOTE, FURNITURE)
class Placement(object):
"""One pack's verdict on one note.
`rule` is the pack-qualified id of the rule that fired (`mc/legend-title`)
so a reader of the spec can find the line of code that made the claim, and
argue with it. A placement that cannot say which rule made it is not
accepted -- an unattributable classification is exactly the kind nobody
can correct.
"""
__slots__ = ('kind', 'rule', 'reason')
def __init__(self, kind, rule, reason):
if kind not in KINDS:
raise ValueError('unknown placement kind: %r' % (kind,))
if not rule:
raise ValueError('a placement must name the rule that made it')
self.kind = kind
self.rule = rule
self.reason = reason
def __repr__(self):
return 'Placement(%r, %r)' % (self.kind, self.rule)
def load(know, get=None):
"""(packs, problems) for the rule packs this knowledge declares.
`packs` is a list of imported pack modules, in declared order. `problems`
is a list of one-line strings naming anything refused; when it is
non-empty `packs` is EMPTY -- a half-loaded rule set would classify some
notes and not others, and the reader could not tell which sentences to
trust. All or nothing, and say why.
"""
if get is None:
from vibe import knowledge as K
get = K.get
declared = get(know, KEY, None) or []
if isinstance(declared, str):
declared = [declared]
problems = []
names = []
for raw in declared:
name = (raw or '').strip().lower()
if not name:
problems.append('an empty name in %s -- remove the entry or '
'name a pack' % (KEY,))
elif name not in PACKS:
problems.append("%s names '%s', which this build does not ship. "
'Known packs: %s'
% (KEY, name, ', '.join(sorted(PACKS)) or '(none)'))
elif name in names:
problems.append("%s names '%s' twice" % (KEY, name))
else:
names.append(name)
if problems:
return [], problems
packs = []
for name in names:
packs.append(__import__('vibe.extractrules.' + PACKS[name],
globals(), locals(), ['classify'], 0))
return packs, []
def classify(text, packs):
"""The first pack that recognises `text`, or None.
First, not best: packs are tried in the order they were declared, and the
reader can see that order in the job file. Nothing here merges two packs'
opinions -- a note that two house styles both claim is a fact about the
declaration, not something this function should quietly average.
"""
for pack in (packs or []):
placed = pack.classify(text)
if placed is not None:
return placed
return None
def apply_to(entries, packs):
"""Split `unresolved` entries into (notes, unresolved) using the packs.
`entries` are the dicts `ticketqc.unresolved_in_view` returns -- each
carries at least 'text' and 'reason'. Every entry comes back in exactly
one of the two lists and none is dropped: `len(notes) + len(unresolved)`
always equals `len(entries)`, which `test_extractrules` asserts directly
because it is the property the whole module rests on.
A `note` gains 'rule' so the spec says which rule claimed it. A
`furniture` keeps its place in unresolved and only its 'reason' changes.
"""
notes = []
unresolved = []
for entry in (entries or []):
placed = classify(entry.get('text') or '', packs)
if placed is None:
unresolved.append(entry)
continue
out = dict(entry)
out['rule'] = placed.rule
if placed.reason:
out['reason'] = placed.reason
if placed.kind == NOTE:
notes.append(out)
else:
unresolved.append(out)
return notes, unresolved
@@ -0,0 +1,118 @@
# -*- coding: utf-8 -*-
"""The `mc` pack: one producer's design-summary habits.
Loaded only when the plant layer declares it (`extract.rule_packs: [mc]`) --
see `vibe/extractrules/__init__.py` for why that is declared and not inferred
from `plant.id`.
WHERE THESE RULES COME FROM
---------------------------
Every rule below was written against text observed on a real sheet, and each
one says which. They are not a model of how this producer draws; they are a
record of lines that actually landed in `unresolved` and what they turned out
to be. A rule with no observation behind it does not belong here -- this file
is evidence, and a guess mixed into evidence is indistinguishable from it
afterwards.
Observed on `WPA1.0` (sample_job, 8" horizontal wall, read 2026-09-01):
all seven notes the generic reader could not place, listed in the order they
sit on the sheet.
WHAT IS DELIBERATELY NOT HERE
------------------------------
`STRIPPING & BLOCKING DETAIL < 25'` is NOT treated as the same thing as the
bare `STRIPPING & BLOCKING DETAIL` beside it. The bare one is the title of a
detail drawn on the sheet and carries no data. The other one carries a
CONDITION -- which detail applies below twenty-five feet -- and a condition is
the part that could change what gets built. So the bare title is furniture and
the conditional one is a note, and the rule that separates them tests for
anything trailing the title rather than matching both with one loose pattern.
That asymmetry is the module's standing rule (`__init__`: when unsure, note).
Nothing here encodes a NUMBER. `*USE MC STANDARD REINFORCEMENT ...` names a
standard this office keeps; what that standard says is plant knowledge and
belongs in the plant knowledge file, looked up by name, never frozen into a
regex in this file. The rule's whole job is to carry the sentence to a field a
person reads.
IronPython 2.7: no f-strings, no pathlib, no typing, no dataclasses.
"""
import re
from vibe.extractrules import Placement, NOTE, FURNITURE
NAME = 'mc'
def _rule(rule_id, pattern, kind, reason):
return (rule_id, re.compile(pattern, re.IGNORECASE), kind, reason)
# Order matters and is not alphabetical: a more specific rule must come before
# the looser one it would otherwise be swallowed by. The conditional detail
# title is tested before the bare one for exactly that reason.
RULES = (
# ---------------------------------------------------------- instructions
_rule('mc/standard-reinforcement',
r"^\*?\s*USE\s+MC\s+STANDARD\s+REINFORCEMENT\b",
NOTE,
'a standing instruction naming this plant\'s standard reinforcement '
'for cut-down panels and openings -- prose, not a number; what the '
'standard says is plant knowledge, looked up by name'),
_rule('mc/conditional-bar',
r"^#\s*\d+\s+BAR\b.*\bONLY\s+NEEDED\s+WHEN\b",
NOTE,
'a conditional reinforcing instruction -- the bar is required only '
'when the condition in the sentence holds, so the condition is the '
'value and it stays as prose'),
_rule('mc/pick-instruction',
r"^USE\s+FOR\b.*\bPICK\b",
NOTE,
'a handling instruction saying which pick this detail is for -- '
'prose, and it qualifies a detail rather than stating a number'),
_rule('mc/detail-title-conditional',
r"^STRIPPING\s*&\s*BLOCKING\s+DETAIL\b\s*\S+",
NOTE,
'a standard-detail title carrying a CONDITION (which detail applies, '
'and when) -- the title alone would be furniture, the condition is '
'not'),
# ------------------------------------------------------------- furniture
_rule('mc/detail-title',
r"^STRIPPING\s*&\s*BLOCKING\s+DETAIL\s*$",
FURNITURE,
'the bare title of a standard detail drawn on the sheet -- names '
'the detail, states nothing about this design'),
_rule('mc/legend-title',
r"^STRAND\s+PATTERN\s*$",
FURNITURE,
'the title above the strand-pattern legend -- the legend\'s DATA is '
'the dimension chain under it, which is read as geometry, not as '
'this text'),
_rule('mc/legend-label',
r"^STRAND\s*$",
FURNITURE,
'a bare column label in the strand legend -- a heading over values '
'read elsewhere, with no value of its own'),
)
def classify(text):
"""A Placement for `text`, or None when this pack does not recognise it.
`text` arrives already whitespace-normalised by the caller (one space
between words, no leading or trailing run), so every pattern here can
assume single spaces and anchor at both ends where it means to.
"""
probe = (text or '').strip()
if not probe:
return None
for rule_id, pattern, kind, reason in RULES:
if pattern.search(probe):
return Placement(kind, rule_id, reason)
return None