WillTools 2026-09-09 -- clean snapshot of the development repo for review
ci / test (push) Successful in 6m14s
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:
@@ -0,0 +1,288 @@
|
||||
# Plan — rebuild Ticket QC around the piece, and fix what it gets wrong
|
||||
|
||||
Two things at once, because they touch the same code and the second is
|
||||
meaningless without the first.
|
||||
|
||||
1. **The button becomes selection-driven**, like Panel Lift Check: preselected
|
||||
assemblies run straight through; nothing preselected gives a list to tick.
|
||||
2. **The checks stop producing false findings** — sixty of them on twelve
|
||||
pieces, roughly a thousand across the job, **all from one cause**.
|
||||
|
||||
Order is not negotiable. **Phase 1 first** — not because it removes findings
|
||||
(it does not; see its corrected acceptance) but because everything phase 4
|
||||
compares is split across the two sheets, so nothing after it can be verified
|
||||
until it lands.
|
||||
|
||||
---
|
||||
|
||||
## What is wrong today, measured
|
||||
|
||||
An audit of twelve WP1 pieces (WP194, 195, 196, 199, 200, 210, 213, 214, 215,
|
||||
219, 220, 412) produced 135 findings. **60 of them are artefacts of ONE bug:**
|
||||
|
||||
| code | count | cause |
|
||||
| --- | --- | --- |
|
||||
| `LGD-MISSING` | 12 | looks for a placed view named `STRAND PATTERN…` / `REINFORCING PATTERN…`. **0 of the 204 WP sheets in this model has one.** The schedule is the BOM table on sheet 1; the per-piece strand count is inside section A-A. |
|
||||
| `FC` | 12 | **the same cause.** `read_ticket` sets `legend_text` only when a legend view is found; with none, it stays `None`, and every comparison against it reports "in the summary, not found on the ticket". |
|
||||
| `FCI` | 12 | " |
|
||||
| `PCF-NOTE` | 12 | " |
|
||||
| `STR-NOTE` | 12 | " |
|
||||
|
||||
An earlier version of this plan split these between the legend premise and a
|
||||
separate two-sheet bug, and gave phase 1 a 48-finding drop to prove itself by.
|
||||
Measured after the union landed: the four legend-dependent codes did not move.
|
||||
**One cause, and phase 2 owns all of it.** The two-sheet fix is still required,
|
||||
for what phase 4 needs — see phase 1's corrected acceptance.
|
||||
|
||||
The remaining findings include one real defect worth the whole exercise, and it
|
||||
is the acceptance test for phase 4 — see below.
|
||||
|
||||
There is also a separate correction in `docs/ticket-audit-wp1-12.md` §4: the
|
||||
97-tickets-are-two-sheets fact is true and still matters; the attribution of
|
||||
those four codes to it was not.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — a ticket is every sheet named after the mark
|
||||
|
||||
**Change:** `ticketqc.collect_context` currently maps a mark to one sheet.
|
||||
Make it map a mark to a **list**, and `read_ticket` read the union: placed views
|
||||
from all of them, sheet parameters from whichever carries them.
|
||||
|
||||
**Do not** special-case `1 OF 2`. Match on the sheet *name* equalling the mark
|
||||
and take everything that matches, however many there are.
|
||||
|
||||
**Acceptance — CORRECTED 2026-08-27, after the change landed.**
|
||||
|
||||
The original text said `FC`, `FCI`, `PCF-NOTE` and `STR-NOTE` would fall from 12
|
||||
to 0 here. **That was wrong, and it was my error, not the implementation's.**
|
||||
Those four compare against `legend_text`, which `read_ticket` sets only when a
|
||||
pattern-legend view is found — and this model has none. They belong to the
|
||||
legend premise, which is phase 2. Measured after the union landed: WP199, WP210
|
||||
and WP412 each resolve to both sheets, `read_ticket` reports two, and all four
|
||||
still fire with `legends_found: 0`.
|
||||
|
||||
What phase 1 must actually show:
|
||||
|
||||
- Every one of the twelve resolves to **both** its sheets, `1 OF 2` and
|
||||
`2 OF 2`, and `read_ticket` reports two.
|
||||
- Sheet parameters — `tkt_length`, `tkt_width`, `tkt_depth`, design number,
|
||||
plant — come back populated, from whichever sheet carries them.
|
||||
- Placed views are the union of both sheets, deduped by element id.
|
||||
- **No regression**: nothing that passed before now fails.
|
||||
- Both suites green: `panellift` and `WillTools.extension`.
|
||||
|
||||
Phase 1 is correct and necessary regardless. A ticket **is** two sheets, and
|
||||
everything phase 4 compares is split across them: it is a prerequisite, not a
|
||||
finding-reducer.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — the schedule is the BOM table, not a legend view
|
||||
|
||||
**Change:** repoint the legend checks. The information they want is:
|
||||
|
||||
- **The bill of materials on sheet 1** — a QTY / MARK / DESCRIPTION table.
|
||||
- **The strand count for this piece** — inside section A-A on sheet 2, printed
|
||||
as `(n) 0010001 (E.L.)`.
|
||||
- **The jacking force** — a `TICKET NOTE - … STRAND JACKING FORCE` view.
|
||||
|
||||
`legend.pattern_prefixes` stays in the knowledge file for plants that do use a
|
||||
legend view; this plant does not, and the check must not fail a ticket for the
|
||||
absence of something the plant never draws.
|
||||
|
||||
**A trap that has already produced a "23x error" report:** the BOM's strand row
|
||||
is a **length in feet**, not a count. Verified on three pieces:
|
||||
|
||||
| piece | BOM row | panel length | BOM ÷ length | section prints |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| WP199 | 321 | 22.9167 ft | 14.007 | (14) |
|
||||
| WP210 | 249 | 24.9167 ft | 9.993 | (10) |
|
||||
| WP412 | 349 | 24.9167 ft | 14.006 | (14) |
|
||||
|
||||
**Acceptance:** this phase now carries **all sixty** of the false findings, not
|
||||
twelve. `LGD-MISSING`, `FC`, `FCI`, `PCF-NOTE` and `STR-NOTE` all fall to 0 on
|
||||
the twelve pieces — all five come from the same missing legend. A new check
|
||||
compares the BOM strand row divided by the panel length against the section
|
||||
count and against the model, and passes on all twelve.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — the button is driven by the selection
|
||||
|
||||
Copy the shape already committed in `PanelLiftCheck.pushbutton/script.py`
|
||||
(`20c6bda`), with one deliberate difference.
|
||||
|
||||
selection -> assemblies, or the assemblies that own the selected members
|
||||
several preselected -> check exactly those, no dialog
|
||||
one preselected -> check that one, no dialog
|
||||
nothing preselected -> a checklist of every assembly, then check the ticked ones
|
||||
|
||||
**The difference from Panel Lift Check:** when nothing is preselected, do **not**
|
||||
ask "batch or single". Go straight to the list. Ticket QC is always a batch of
|
||||
one or more; the mode question does not exist here.
|
||||
|
||||
The list needs to be usable at 238 pieces: show mark, design number, and whether
|
||||
a spec or only a regex summary backs it — the information the current
|
||||
design-number dialog shows, moved onto the pieces.
|
||||
|
||||
**The engine has to change with it.** `ticketqc.run(doc, design_numbers=...)`
|
||||
walks whole designs. Add a path that takes **assemblies** and resolves each
|
||||
piece's design intent per piece. A worked read-only example of exactly this is
|
||||
`_scratch/claude_tkt_run.py`. Keep the design-number entry point working —
|
||||
`tools/sweep.py` and `ticketchecks.check_handling_result` both use it.
|
||||
|
||||
**Acceptance:** with three assemblies selected, the report covers three pieces
|
||||
and names them. With nothing selected, the list appears, and ticking three gives
|
||||
the same report. Selecting a *member* of an assembly works like selecting the
|
||||
assembly.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — the checks that were never there
|
||||
|
||||
All of these are arithmetic between two values that already exist. None needs a
|
||||
drawing to be read; none needs judgement. Add them one at a time, each with its
|
||||
own test.
|
||||
|
||||
### 4.1 Handling stations against the geometry
|
||||
|
||||
**This is the one that found a real defect, and it is the reason for the whole
|
||||
refactor.** WP199's ticket prints dunnage at `4'-9"` from each end. The piece is
|
||||
22'-11", so the blocks go at 4.75 and **18.17 ft**. `OPENING 1` runs **15.60 to
|
||||
18.96 ft**. The ticket tells the yard to set a block where **74% of the width is
|
||||
missing** — S of 403 in3 against 1,525 in3 for the full section.
|
||||
|
||||
To do this in code:
|
||||
|
||||
- Read the three handling diagrams' dimensions. They are `Dimension` elements in
|
||||
the `TICKET NOTE - PANEL - DUNNAGE` / `- ERECTION` / `- PICKING/STRIPPING`
|
||||
views. **Use the `Value`, not `ValueString`** — these dimensions carry a
|
||||
`ValueOverride` of `U+200E` and `ValueString` is not reliable.
|
||||
- **Close the chain.** Printed segments do not sum to the piece length; the
|
||||
difference is a segment the ticket deliberately does not dimension. That is
|
||||
correct and must not be reported. Recover it:
|
||||
`missing = length - sum(printed)`. WP412: `2'-6" 7'-6" [blank] 7'-6" 2'-6"`
|
||||
sums to 20'-0" on 24'-11"; the recovered 4'-11" gives stations 2.5, 10.0,
|
||||
14.917, 22.417 — which are the four modelled P136 exactly.
|
||||
- For each **dunnage** station, ask what section is left there. An opening under
|
||||
a dunnage block is the finding. **Dunnage points are not inserts** — there is
|
||||
nothing in the model to compare a dunnage station against, only geometry.
|
||||
- For each **picking** station, compare against the modelled insert stations.
|
||||
|
||||
**Insert stations:** `read_ticket` gives each lifter a `local` tuple. There is
|
||||
no `x` or `y` key — asking for one returns `None` and looks like missing data.
|
||||
`local[0]` is measured from the panel **centre**:
|
||||
`station = length / 2.0 + local[0]`.
|
||||
|
||||
**Acceptance:** WP199 raises the dunnage-in-opening finding with those numbers.
|
||||
The other eleven do not raise a false one. WP412's four picking stations match
|
||||
the model to the inch.
|
||||
|
||||
### 4.2 Weight, three ways
|
||||
|
||||
Not a comparison — an agreement between three numbers, and **the pattern of the
|
||||
disagreement is the diagnosis**:
|
||||
|
||||
| | derived from |
|
||||
| --- | --- |
|
||||
| **printed** | the WEIGHT (lbs.) field in the title block |
|
||||
| **reconstructed** | l x w x t less blockouts plus cast extras, x unit weight |
|
||||
| **measured** | the modelled solid's volume x unit weight |
|
||||
|
||||
- b and c agree, a differs -> the ticket's weight field is stale.
|
||||
- a and b agree, c differs -> something is modelled that the dimensions do not
|
||||
describe.
|
||||
- a and c agree, b differs -> **our** reading of the geometry is wrong. Suspect
|
||||
the tool first.
|
||||
|
||||
`liftexport.weight_check` already computes all three as `stated`,
|
||||
`reconstructed`, `solid`. What is missing is reporting *which* one disagrees.
|
||||
|
||||
**Acceptance:** WP219 printed 15,525 / model 15,525 and WP220 printed 15,371 /
|
||||
model 15,371 both come out clean. WP199 printed 24,345 / model 24,348 is inside
|
||||
tolerance. The five pieces the earlier sweep flagged (WP187, 192, 313, 409, 411)
|
||||
each report which of the three disagrees.
|
||||
|
||||
### 4.3 The rest, in descending value
|
||||
|
||||
| check | what it compares |
|
||||
| --- | --- |
|
||||
| BOM row vs assembly members, family by family | needs `SuperComponent is not None` -> skip, or nested children double-count. `SR5602` sits inside `P76`, `RL_OVAL_PATCH_4T` inside `QLPD4T5WG`, `P106` inside `JVI PSA 6035` |
|
||||
| Every placed view's name refers to this design | `TICKET NOTE - WP2- STRAND JACKING FORCE` is on 100 WP1 sheets. Its **content** is WP1's own number, so report the name, never infer the content is wrong |
|
||||
| Invisible characters in ticket text | `U+200E` sits in the design summary and crashed a tool that printed it |
|
||||
| Dimension chains close | every chain, against the piece's own dimension. Report the recovered segment, not a defect |
|
||||
|
||||
---
|
||||
|
||||
## Ground truth for the tests
|
||||
|
||||
Measured from the live model and the rendered tickets. Use these as fixtures.
|
||||
|
||||
| piece | length ft | width ft | thick | weight (ticket / model) | strands | BOM strand ft |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| WP199 | 22.9167 | 11.9167 | 8" | 24,345 / 24,348 | 14 | 321 |
|
||||
| WP200 | 22.9167 | 4.8333 | 8" | 9,389 / — | 6 | 138 |
|
||||
| WP210 | 24.9167 | 8.4167 | 8" | 20,950 / — | 10 | 249 |
|
||||
| WP213 | 21.9583 | — | 8" | — | 14 | 307 |
|
||||
| WP215 | 18.9167 | 11.9167 | 8" | 22,192 / 22,192 | 14 | 265 |
|
||||
| WP219 | 18.4583 | 8.4167 | 8" | 15,525 / 15,525 | 10 | 185 |
|
||||
| WP220 | 18.4583 | 8.3333 | 8" | 15,371 / 15,371 | 10 | 185 |
|
||||
| WP412 | 24.9167 | 11.9167 | 8" | 29,291 / 29,291 | 14 | 349 |
|
||||
|
||||
Handling dimensions as printed:
|
||||
|
||||
| piece | picking | erection | dunnage |
|
||||
| --- | --- | --- | --- |
|
||||
| WP199 | — | — | 4'-9" each end |
|
||||
| WP200 | 4'-3" each end | 9" | 4'-9" each end |
|
||||
| WP210 | 2'-6" 7'-6" [blank] 7'-6" 2'-6" | 1'-8" | 5'-2" each end |
|
||||
| WP213 | 2'-0" 6'-0" [blank] 7'-4" 2'-0" | 2'-3" | 4'-7" each end |
|
||||
| WP215 | 3'-10" each end | 2'-3" | 3'-11" each end |
|
||||
| WP219 | 3'-9" each end | 1'-8" | 3'-10" each end |
|
||||
| WP220 | 3'-9" each end | 1'-3" | 3'-10" each end |
|
||||
| WP412 | 2'-6" 7'-6" [blank] 7'-6" 2'-6" | 2'-6" | 5'-2" each end |
|
||||
|
||||
Insert stations from the model, along the length:
|
||||
|
||||
| piece | P136 | P76 |
|
||||
| --- | --- | --- |
|
||||
| WP213 | — | 2.75, 8.0, 12.625, 19.958 |
|
||||
| WP219 | 3.75, 14.708 | 2.75, 4.25, 14.208, 15.708 |
|
||||
| WP220 | 3.75, 14.708 | 2.75, 4.25, 14.208, 15.708 |
|
||||
| WP412 | 2.5, 10.0, 14.917, 22.417 | 3.5, 10.0, 14.917, 21.417 |
|
||||
|
||||
WP199 `OPENING 1` runs 15.60 to 18.96 ft.
|
||||
|
||||
---
|
||||
|
||||
## Not in scope, and why
|
||||
|
||||
These need an answer nobody has written down. **Do not decide them in code.**
|
||||
|
||||
- Whether the erection dimension landing on a `P136` (4 ton) rather than a
|
||||
`P76` (8 ton, and the mark the BOM itself calls "Erection Anchor") is a
|
||||
problem. It happens on WP412. On WP219 and WP220 the printed erection station
|
||||
lands on **no insert at all**. Report as a question; do not fail a ticket on
|
||||
it.
|
||||
- The interference acceptance. The check flags shared material over 0.125";
|
||||
`P136 x SR5602` overlaps 0.21–0.23" on nine of twelve pieces.
|
||||
- Whether a family pair repeating across nine pieces is one problem or nine.
|
||||
|
||||
---
|
||||
|
||||
## Working rules
|
||||
|
||||
- **Read only.** Ticket QC touches nothing. No `Transaction` anywhere in this
|
||||
work.
|
||||
- Both suites stay green, every phase: `panellift` and `WillTools.extension`.
|
||||
- `WillTools.extension/` is **IronPython 2.7**: no f-strings, no pathlib, no
|
||||
dataclasses.
|
||||
- `python` is not on PATH. The interpreter is the Python 3.12 install under the
|
||||
current user's `AppData\Local\Programs`; `tools/rvt.py` and the test commands
|
||||
in `CLAUDE.md` both spell it out.
|
||||
- Run `python tools/scrub.py` before committing. No client or job name in the
|
||||
repo, ever.
|
||||
- **Commit each phase separately.** A phase that cannot be verified on its own
|
||||
is a phase that gets debugged inside the next one.
|
||||
- Push nothing. Will decides.
|
||||
Reference in New Issue
Block a user