A synthetic permit-set estimator surface with /diff, /takeoff, and a 7-file export ZIP. Scaffold a new estimator project with:
ck new <name> --template estimator-demo --host <name>.highline.work
cd ~/Work/<name>
python3 validate_estimator.py # all PASS expected
.venv/bin/uvicorn server.serve:app # local preview at :18211
What is in the box
| Path | Purpose |
|---|---|
content/sets/demo-permit-2026-05.json | Seed set A. Synthetic 1200 sqft single-story residence at 100 Demo Lane, Synthville, 8 rooms, 12 openings (4 doors, 8 windows). Schema version creator_kit.estimator.construction_set.v1. |
content/sets/demo-permit-2026-06.json | Seed set B, revision 1. Identical to set A except for one added window in the mech room. The two-set pair gives the /diff route a non-trivial delta. |
content/expected/diff-2026-05--2026-06.json | Precomputed diff fixture loaded by GET /diff. The demo template returns this verbatim; a real fork would compute it from the two sets at request time. |
content/expected/takeoff-2026-05.json | Expected takeoff for set A. The validator regression-checks the live-computed takeoff against this. |
server/serve.py | FastAPI server, five routes plus static mounts. Basic Auth (anonymous in local-dev). |
server/render.py | Construction-set loader, deterministic takeoff computation, diff lookup, export-ZIP builder. Stdlib-only (zipfile plus csv plus io). |
server/templates/{base,index,diff,takeoff}.html | Jinja2 templates for the HTML views. /diff and /takeoff content-negotiate: JSON by default, HTML on ?format=html or Accept: text/html. |
public/_system/{_system,_patterns,_example}.css | Three-stylesheet load order. _system.css is the vendored Candlefish Design System; _patterns.css is the kit-authored semantic-tokens layer; _example.css carries the estimator-demo-specific extensions. |
validate_estimator.py | Per-template validator. Five check groups: fixture integrity, voice rules, /diff JSON shape, /takeoff math (regression vs expected fixture), /export.zip 7-file contract. Stdlib-only for fixture and voice; fastapi.testclient.TestClient for routes (auto-skips with FLAG when fastapi unavailable). |
tests/test_estimator.py | 24 stdlib unittest tests across loader, takeoff math, diff, export ZIP, validator, and routes. Routes auto-skip if fastapi is unavailable. |
tests/visual-targets.json | Three visual-regression targets (index, diff, takeoff). Read by runtime/visual_regression.py (kit-level vendored harness). |
ck.config.json | Template metadata consumed by ck new (name, default surface pattern, validator command, server command, port 18211 to avoid collision with essay-series at 18210). |
Routes
| Method | Path | Returns |
|---|---|---|
| GET | /health | {"status":"ok","sets":<count>}. Open, unauthenticated. |
| GET | / | HTML index listing seed sets, links to /diff and /export.zip. |
| GET | /diff?a=<set>&b=<set> | Precomputed diff JSON. Top-level keys: summary, sheets, rooms, openings, assemblies, structural_callouts, bom_delta, caveats. Returns HTML with ?format=html. |
| GET | /takeoff?set=<set> | Live-computed takeoff JSON. Top-level keys: set_id, project_name, address, components, loose, caveats. Returns HTML with ?format=html. |
| GET | /export.zip?a=<set>&b=<set> | 7-file ZIP: summary.txt, by-position/{loose-estimator,component-designer,gc-framer}.txt, csv/{findings,takeoff,cut-list}.csv. |
| GET | /_system/<path> | Static CDS chrome assets. |
Takeoff formulas (deterministic, simplified)
lumber_linear_ft = sum(assembly.linear_ft)
drywall_sqft = interior_lf * 18 + exterior_lf * 9
roof_sqft = total_finished_sqft * 1.15
doors_qty = count of openings where kind == "door"
windows_qty = count of openings where kind == "window"
A real estimator-demo fork replaces these with a real takeoff engine. The route shape (set_id, project_name, components, loose, caveats) is the contract that survives the swap.
Diff is precomputed
GET /diff loads content/expected/diff-<a>--<b>.json and returns it verbatim. The demo template only ships a precomputed diff for the (2026-05, 2026-06) seed pair. A real fork computes the diff from the two sets at request time, matching the same response shape.
Out of scope (v1)
- Real DXF or MMDL parsing. The template is fixture-driven.
- Multi-revision diff chains. The kit only ships a single diff fixture.
- Engineer-of-record overlays, structural-authority resolution, fidelity gates. The cassidy product (BWH) implements these on a richer schema; the kit’s
structural_calloutsfield is empty in both seed sets. - Real PDFs in the export bundle. The seven files are plaintext placeholders generated from the fixtures.
- Per-position cost rollups. The export’s three
by-position/files contain the same component counts; a real fork differentiates them.
What forking this template gets you
A working FastAPI surface with three load-bearing patterns the kit standardizes:
- JSON route plus content-negotiated HTML view.
_wants_html()inserver/serve.pyis the pattern; it works for any route that has both an API consumer and a browser consumer. - Deterministic takeoff plus precomputed diff. Lets the validator regression-check the math without a live server, and lets the export bundle be reproducible.
- Per-template validator with FLAG-on-skip.
validate_estimator.pyis the model: stdlib checks always run; deps-required checks FLAG-skip cleanly sock validate --offlineagainst a fresh checkout does not hard-fail on missing pip installs.
Visual regression
tests/visual-targets.json ships three baselines: index (full page), diff (HTML view, #diff-summary selector, 1900x1100), takeoff (HTML view, full page, 2-second settle for any future chart rendering). The PNG baselines are committed at tests/visual-baselines/estimator-demo/{index,diff,takeoff}.png. Playwright is installed by the kit’s bootstrap.sh.
See also
- essay-series — longform writing template.
- morning-brief — multi-connector daily brief Wake-derivative.