← back to pin list

7g. Customer self-serve surface — upload, educate, heal, approve, quote

kerfmaster pin list · UI / workflow decisions

rulingmixedreads as factA decision by Aristide or Jordan. True because it was decided; it can be superseded, but it cannot be stale.

Content last changed 2026-08-22 — computed from the item itself, not typed.

Contract — Intake stage

This contract states a decision, not an implementation.

takes customer-file

makes identified-file

fails if Letting a customer approval bypass the operator, or asking the customer to approve anything sacrificial (tabs, leads, cut order) — they approve pricing and bridges, nothing else.

Contract last changed 2026-08-21 — computed, not typed. Dated separately from the text above, so neither date can speak for the other.

Raised and ruled by Aristide, 2026-08-21 (Telegram discussion). The surface is a COMPOSITION of pins that already exist — it adds no new pipeline stage: upload + preflight (14, 14d), education on the form (14e), auto-heal delivered as a comparison-board approval artifact (house style, item 6), automated quoting (15–15e), and the operator picking up from the saved result (7d: “it moves the start, it doesn’t move the program”).

The three rulings, verbatim

  1. Units: “They can pick at the start with a toggle: metric vs imperial.” — the unit system is the customer’s explicit choice at the top of the upload form, never inferred. Closes the units half of 14b; the stated-finished-dimension field (with the “drawn 1:1 at actual size” option) stays per 14d. Consistent with 7e’s per-user display units inside the tool.
  2. Approval scope: “customer approves pricing and bridges, not the rest. you can show them, but be clear that that can be overridden by the operator.” — the customer approves what is PERMANENT in their part: the price, and the bridges that stay in the finished piece. Everything sacrificial or process-side — tabs, nubs, pierce marks, leads, cut order — is shown for transparency on the approval sheet, in customer language, labelled as subject to operator override. The approval line lands exactly on the permanent-vs-sacrificial line from the tab/bridge definitions.
  3. Human gate: asked whether a customer-blessed file still always gets a human eye before the laser — “yes.” Approval is a gate the job must pass, never a bypass around the operator.

Wiring is not blocked by the worksheet

Aristide, same message, on 15e: “he is not altering your tables, just filling it in.” Correct — Jordan supplies ROWS, never structure, and the table discipline (a missing value raises, never guesses — the beam_comp precedent) is designed for exactly this order of work: the quote engine can be wired now and simply refuses to print a dollar figure until the row lands.

Build state

Nothing of this surface exists in code (risky-flagged for that reason). The engine pieces it composes exist to different degrees: preflight checks run as scripts, heal/board/nest/post run end-to-end, no quote engine, no upload form, no approval artifact. Phase note: engine wiring (quote calculator, preflight-as-flow, the approval dataset) changes what we can prove and tell a customer — in phase; the customer-facing UI itself stays under the 2026-08-02 phase ruling until Aristide amends it — put to him 2026-08-21.

⚑ 2026-08-21, same day: Aristide clarified the intent — “not just the interface, the structure so we can build the interface on something solid” — and agreed the build order: quote engine → fast-estimate tier + calibration → preflight as a flow → approval artifact. Piece one (quote engine: rates.json, tables.rate()/RateMissing, quote.py) is built — see 15/15a/15b build states.

⚑ 2026-08-21, piece 2: fast-estimate tier + calibration harness built on Aristide’s go (“go for it”) — see 15d build state. Order remaining: preflight as a flow → approval artifact.

⚑ 2026-08-21, piece 4 (Aristide: “go”) — the approval artifact is built, completing the agreed four-piece order. approval.py: ONE dataset (approval.json, canonical inches) rendered as customer sheet + shop sheet under viewer/approval/<label>/ (+ /shop/), per the client-shop-split ruling — every customer sentence is generated by a sentence function from the measured numbers and the shop sheet calls the SAME functions to quote the customer’s exact words back, so the pages cannot drift. The three rulings wired: (1) --units imperial|metric stands in for the toggle, recorded in the dataset, converted at render time only (shop sheet always inches/thou); (2) approve items = price + bridges (customer word: “connecting tabs — they stay in the finished piece”); break-off tabs, cut start points and cut order are shown under “our operator may adjust these”, values from the tables where the row carries them, said-not-guessed where it doesn’t; (3) the human-gate sentence is on both sheets and approval is a status gate, never a bypass. A BLOCKED preflight renders the one intake question instead of an approval (a held job is never offered for approval); the bridge plan is placed AND verified (raster re-test on the bridged geometry — a loose island puts the job ON HOLD). The price approve-item has three states (firm / indicative / to_follow) and can reach FIRM only once the roll-up from 15a line items to one figure is ruled — recorded as open question Q29 (answers: Aristide/Jordan). Specimens live: /approval/daisy-approval/ (12 bridges on 3 islands, held 3 / loose 0), /approval/novi-approval/ (none needed — the design carries its own), /approval/daisy-metric/ (metric toggle), /approval/blocked-test/ (raster block).

⚑ 2026-08-22 — the approval artifact’s price section now shows ONE indicative figure through the Q29 roll-up (Q29 RULED, Aristide 2026-08-21: every pricing knob is a per-shop table variable; handling bills at a separate operator rate by default). approval.py builds fast-tier line items (fast_estimate.rollup_items, quantity via --qty, recorded in the dataset) and runs rollup.compose. A composed total renders state indicative: the customer sheet says the plain high-side figure (“Estimated price for N pieces: about $X. We estimate on the high side on purpose…” — no internals vocabulary), the shop sheet carries the full steps trail, assumed_rows, the policy knobs used and each line item’s basis note, quoting the customer sentence back per the split ruling. A blocked compose renders state to_follow with the exact missing rows. Verified on BNM76282 qty 15: with the live table it blocks on exactly policy.operator_hourly_rate_usd — the OWED row, correct behaviour; with a CADMASTER_TABLES test overlay supplying an operator rate it composed $1275.40 (arithmetic hand-checked step by step) and the 18 seed-strength rows forced INDICATIVE. The fast figure can never read firm: only quote.py on the posted NC through the same skeleton can, and that wiring is the remaining piece.

Three more rulings — Aristide, 2026-08-22 (Telegram), verbatim

  1. Seed prices in front of customers: asked whether the customer sheet should stay at “price to follow” unless every rate feeding the figure is measured or decided — “customers will never see this until we go live, so we don’t need to hedge agains that, and yes, we can therefor use seed data.” The proposed suppression gate is DECLINED; pre-go-live, the customer sheet shows the indicative seed-composed figure. Caveat that keeps this honest: the strength audit reads the row’s source block, and values must arrive through the rates editor (which rewrites provenance wholesale on save) — a value hand-typed into rates.json under a row whose source block predates it would inherit that block’s strength. Verified 2026-08-22: a test operator rate saved with assumed provenance is audited (18 assumed rows, operator rate among them).
  2. Entry point — per-shop upload sites: “Each customer we will give them a ‘upload site’ on their either their own subdomain we assign them, or they can white label on their own sub domain.” The customer surface is MULTI-TENANT from day one: every client shop gets its own upload site, on a subdomain we assign or white-labelled on the shop’s own domain. (Consistent with the per-shop tables direction — a tenant is a shop with its own rates, branding and hostname.)
  3. Customer identity & payments: “as we will need to email customers, they would have to create an account with minimal info at least to create a quote, and when they order, for our own online solution, we would possibly have to set up a payment portal through stripe. for now we skip the stripe part until we have more clarity on the payment portal. we are building it also for our clients, and they will have their own payment portals. we need to be flexible in that setup. we would not be collecting payments on their behalf.” So: an account with minimal info is required to create a quote (email is the contact channel); Stripe is DEFERRED for kerfmaster’s own online solution; each client shop keeps its own payment portal and the setup must stay flexible — kerfmaster never collects payments on a shop’s behalf.