Description
ScopeMaker — construction scope-of-work exhibits, generated properly. Pick a CSI MasterFormat division, choose from a curated clause library (universal obligations plus trade-specific inclusions, exclusions and clarifications), edit anything you need, and export a paginated PDF, editable Word, Markdown or JSON — all carrying identical clause numbering.
Cross-referenced spec sections mean a Division 21 package is automatically offered the Division 07 firestopping and Division 08 access doors it actually carries — so you stop paying for scope gaps later. Every line is an editable, reorderable, numberable item, rendered from one numbered tree so clause 3.2.4 means the same sentence in every export.
Free & open-source. See the docs and get it on GitHub.
= v1.5.6 — August 10, 2026 =
Test infrastructure and dependency currency. No user-facing change.
## The browser JavaScript is tested now
The editor's reordering had no automated coverage at all. That is how 1.5.0
shipped keyboard moves that silently did nothing, and how 1.5.5's fix went out
verified only by driving a browser by hand.
Vitest + jsdom, 13 tests, its own CI job. They load the real
`scopemaker/static/js/app.js` — not a copy, not an extracted helper — so the
file that ships is the file under test.
**It immediately found a bug in the 1.5.5 fix.** Moving a child down let it
escape its parent: in `[a, a1, a2, b]`, clicking `a1` down put it *after* `b`.
"Is this a descendant" was decided by comparing parent ids against the item
being moved — but a shallower node further down the list also has a different
parent. Descendants are now found by walking the parent chain, which handles
grandchildren too. Both cases are tested.
Two of the harness's own mistakes are worth recording, because each produced
convincing failures that were not real:
- Reusing one document across tests. `app.js` registers delegated listeners on
`document`, and clearing `document.head` removes the script element but not
its listeners — so handlers stacked and one click fired several moves.
- A freshly constructed JSDOM is still `loading`, so `ready()` defers to
`DOMContentLoaded`, which fires after a synchronous click. Every test
reported that nothing happened.
One window per test, and an explicit `DOMContentLoaded`, fixed both.
## Python 3.14
Dependabot offered `python:3.12-slim` → `3.14-slim` for the Docker image, and
it was green — but only because the Docker job builds the image and runs
`check-pdf`. The test matrix stopped at 3.12, so nothing exercised the
application on the interpreter the container would actually ship.
3.14 is in the matrix now and the suite passes on it, so the bump is backed by
something. Merged after that, not before.
## Coverage
- `mail.py` **74% → 97%**: STARTTLS upgrade and authentication, implicit SSL not
double-negotiating, and a dead relay returning `False` rather than turning a
password reset into a 500.
- `projects/routes.py` **71% → 96%**, including the guard that refuses to delete
a bid package with scopes still attached.
## Fixed
`config._bool` falls back to its default on a blank value, matching `_int` and
`_csv`. An emptied line is how an operator says "not configured", and
`.env.example` ships blank entries — so `OIDC_REQUIRE_VERIFIED_EMAIL=` would
have silently disabled a security check that defaults to on. Trusted input, so
not a vulnerability, but the inconsistency could bite.
## Dependencies
Python base image 3.12 → 3.14, vitest 2 → 4, jsdom 25 → 30, `setup-node` 4 → 7,
`upload-artifact` 4 → 7, `upload-pages-artifact` 3 → 5, `build-push-action`
6 → 7. npm added to Dependabot — dev-only, but still a supply-chain surface.
553 Python tests, 13 JavaScript, 87% coverage.
**Full changelog:** https://github.com/ibuilder/scopemaker/blob/main/CHANGELOG.md
Reviews
There are no reviews yet.