Add project estimate: consumables, labor, and suggested selling price
The Cost tab now produces a full quote, not just material cost.
- estimate.py: project_estimate() = materials (incl HST) + consumables
(screws per butt joint, glue per M&T connection / dado / rabbet, finish
$/sq ft of finished surface) + labor (editable minutes per operation —
setup, cut, butt joint, assembly, sanding, and per-feature tenon/mortise/
hole/slot/dado/rabbet/chamfer — × counts from the scene/plan, × shop rate).
- Selling price = MARGIN on total cost: price = total_cost / (1 - margin),
labor counted as cost. A target price overrides margin and back-solves the
implied margin. EstimateRates persisted to estimate.json.
- Cost tab: live margin % spinbox + target $ field, "Edit rates…"
(RatesEditDialog), existing "Edit prices…" / "Refresh from Kent…", Print.
- All counts are deterministic (count_ops off scene.joints / connections /
features / finishes); nothing guessed.
- tests: op counts, screws/glue, labor scaling, margin formula, target
back-solve, div-zero guard, rates roundtrip, format, and GUI cost-tab +
margin/target controls. 163 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>