kerfmaster pin list · UI / workflow decisions
recordStates what the code does today. Auditable against the code — this is the only type that can go stale.
Content last changed 2026-09-03 — computed from the item itself, not typed.
Built 2026-09-02 (Jordan, after standing the 29 Lathrup parts up one by one was the alternative). The move: POST /api/jobs/<id>/partrotate {mode: 'rule' | 0 | 90} on a part-list job applies one import-rotation choice to EVERY distinct part the list references -- per part exactly the brick-34 move (force set or cleared, pipeline re-run), so each part stays individually overridable on its own Intake tab afterward: seed, never bind. Untouched when unchanged: the frame test is _decide_introt itself on a probe of the part's own source (one decision function -- master and per-part can never disagree); a part already lying in the asked frame keeps its downstream stages, overrides and heal edits -- a master click must never needlessly drop the work of 27 parts whose frame does not turn. The choice is still recorded on the part (force marker) and on the list (partlist.json frames: mode+date). Stale rule: if any frame actually turned, the packed layout drops like a list edit (a stale pack never keeps serving) and the posted sheet programs drop with every O-number kept; nothing turned = the layout stands. Honest per-part reporting: sketch rows skip WITH the 'typed as drawn' words, never silently; every result carries the part, its frame and its note. UI: 'part frames' select + 'Apply to all parts' on the Part list tab (follow shop rule / all as drawn / all stood 90), last-applied date shown, toast reports N re-healed / M already in frame / layout dropped. Proven: test_partrotate.py 19 controls (refusal ladder verbatim, duplicate rows collapse to distinct parts, only the turning part re-heals, wide part provably untouched, layout stands on a no-change apply and drops on a turn, master-then-per-part seed-never-bind round trip, sketch skip words) + neighbor suites green (introt 20, pl_deliver 27, pl_seq 27, sheet_intake 36, part_list 24, pl_post 26, sketch 21, healedit 23). Honest limits: the master sets the IMPORT frame -- the pack's own rotation step stays the Rotate column per row; who applied awaits operator accounts.
2026-09-03 (Jordan: 'If I'm selecting follow shop rule, how do I know what the shop rule is?'): both 'follow shop rule' selects (Part list master + per-job Intake tab) now NAME the rule in the option itself — 'follow shop rule (lay tall parts down)' / '(stand wide parts up)' / '(no rotation)' — read live from the shop profile (app.js shopRuleShort()); the rule is visible without opening Shop setup.
2026-09-03 (Jordan: 'I see that YOU did it, but WE need to be able to do it'): the master NEST-ROTATION switch joins the Part list tab beside part frames — one select (locked / 0-180 / quarter) + 'Apply to all rows + pack' sets the Rotate column on every row, saves the list (layout drops per the stale rule) and re-packs in one click. The distinction stated on-screen: part frames decide how a part lives on its own tabs; the Rotate column is what the PACK obeys — rows at quarter license the packer to stand parts up for yield regardless of frame (exactly what made the frames button look dead). Client-side only over the existing partlist save + partnest endpoints; proven live with a real-mouse Playwright click (29 rows set, repacked 10+8+11, every placement at 0/180).