TheRON cdde394bd8 composer: /m/stl, a bounded export route
The route lives under the gated prefix from IDENTITY-CONTRACT.md section 5, so
gating it later is a proxy change and not a code change. It ships OPEN, because
/m/ is not yet gated and no membership system exists to gate it -- recorded in
section 8 of that document and cited in the module docstring, so a successor
reads it as a decision rather than an oversight.

model/stl with a Content-Disposition filename carrying the input_id. The
browser saves it without JavaScript assembling a blob on the happy path. The
route rebuilds from the query parameters rather than caching what /api/build
just made: the file is a function of the URL, and the composer goes on holding
no state between requests. Persistence is its own piece of work and should not
arrive here by accident.

MECHCOMP_MAX_EXPORT_MM bounds it, defaulting to 3048 -- one ten-foot member.
The ceiling exists because this endpoint is unauthenticated on a public name
and member_length_ft is an unbounded number whose value alone decides the size
of the computation and the download. /api/build has the same exposure with a
constant-size answer; export is where bounding becomes worth it.

3048 is not a claim that longer members are wrong. A hundred-foot member is a
real artifact and nobody prints one in a piece -- it gets sectioned. The export
ceiling and the member catalogue answer different questions and conflating them
would be the mistake. The limit is configuration rather than a query parameter
because a limit the caller can raise is not a limit, and an unparseable or
non-positive setting falls back to the default rather than disabling the bound:
a typo must not leave a limit that exists in the documentation and nowhere else.

Over the ceiling is a 413 naming the length, the limit and the key, and it
refuses rather than truncates. A truncated export would ship a 3048 mm file
whose design record describes a 30480 mm member -- the same silent wrongness
the length_view control was added to prevent, arriving by a different door.

ProfileRejected is a 422 with the reason intact, an unknown family a 404, and
only a genuinely unexpected exception a 500. Refusals are text/plain so the
download control can show them and a person who hit the URL by hand can read
it. A rejection is the compiler working.

Two things changed while building rather than after. The type coercion was
extracted from build_payload into typed_overrides and is now shared: had the
export coerced differently from the view, the downloaded file would not be the
part on screen, and neither would have looked wrong on its own. And the button
re-sends the query the current drawing came from rather than reading the
controls when pressed, so a half-typed number in a text box cannot export
something that was never displayed.

The page's JavaScript was parsed with node --check before landing. The
download handler rebalanced braces around the data.ok block, which is exactly
the kind of edit that compiles as a Python string and breaks in a browser.

16 tests. Mutation-proven: never applying the ceiling fails 4, truncating
instead of refusing fails 4, a bad environment value disabling the bound fails
5, the export using its own coercion fails 1, a rejection becoming a 500 fails
2. Restoration verified by checksum, PYTHONDONTWRITEBYTECODE=1 throughout.

One narrow margin worth recording: the shared-coercion guarantee rests on a
single test, test_the_export_is_the_part_the_view_shows. It is the only thing
that failed under M4. Deleting it would silently remove the only check that the
file matches the drawing.

Suite 643 passed.
2026-09-12 11:00:11 -05:00
2026-08-14 09:21:31 -04:00

Mechanical Compiler

Build the capacity to construct real structures from reclaimed and commodity materials, using whatever fabrication is actually to hand — 3D printing, tabletop CNC, welding, cement casting, COTS stock.

The reference case is a faceted timber shell: planar panels meeting along straight fold lines, converging on nodes, sitting on a platform. Buildable without a factory, provided someone has worked out what the pieces are and how they meet. That last clause is the project. The Mechanical Compiler exists to make the pieces computable, qualifiable, and reproducible by someone who was not present when they were designed.

Scope

In scope: geometry, qualification, and reproducibility of structural members and their interfaces.

Not in scope: building codes, permitting, jurisdictional approval. Qualification and compliance are different things. A qualification says this member is what it claims to be, made this way, from this stock. Compliance is a jurisdiction-specific argument someone else may build on top.

The project records physical claims, never verdicts — section modulus, moment of inertia, material provenance, process parameters. Those are what any future argument would need. A pass/fail verdict would bake in a jurisdiction we do not want to be bound to. Measure and attest; never adjudicate.

Repository layout

docs/                     specification, state, failure log, roadmap
legacy/openscad/          rev 8.0.0 generators — reference, not a live target
fixtures/                 frozen acceptance oracles
tools/reference-toolchain/  pinned OpenSCAD + BOSL2, build-time only
src/mechcomp/             the application
tests/                    acceptance against the oracle

Read docs/ROADMAP.md first for what this is, then docs/ENVIRONMENT.md for how an instance is built. If you are writing provisioning automation, read docs/FAILURES.md before the specification — every entry is something a script written from the specification alone would have got wrong.

Getting started

make deps            # virtualenv, base + CAD requirements
make verify-oracle   # confirm the frozen oracle is intact
make test            # pytest -n auto

make verify-oracle needs nothing but Python. It should pass on any machine at any time; if it does not, stop.

The oracle

fixtures/strap-beam-8.0.0/ holds 123 frozen cases — 113 accepted, 10 rejected — produced by OpenSCAD 2021.01 with BOSL2 at 92d697c2. It is the acceptance criterion for any reimplementation of the generators.

The ten rejected cases are part of the contract. A port that accepts them is wrong, however good its numbers look elsewhere. It is easy to reproduce the geometry and quietly lose the constraint that made it trustworthy.

The generators under legacy/openscad/ are the reference implementation, kept so the oracle can be regenerated. They are not a live target and the running application has no OpenSCAD dependency.

Provenance

This project's documents, code and roadmap are LLM-generated under human direction. That is stated plainly here and in any downstream submission. We do not obscure it.

The maintainer reviews and owns every line. Anything that could not be defended in a review thread does not ship.

Licence

AGPL-3.0-or-later. See LICENSE.

Section 13 obliges us to offer source to users interacting over a network, so any deployed web tier carries a visible link back to this repository. That is a licence obligation, not a courtesy.

S
Description
No description provided
Readme AGPL-3.0
1.1 MiB
Languages
Python 81%
OpenSCAD 18%
Shell 0.5%
Makefile 0.3%
Dockerfile 0.2%