ROADMAP.md was described as a reference rather than a commitment -- something
that documents intent and will eventually become a roadmap. The header now says
so, and says that HANDOFF section 4 holds the actual order. Sections 1, 2, 3 and
7 are durable and still correct; section 4's sequence and section 5's production
assumptions have drifted since 18 AUG.
Principle 17: nothing approximate may write to anything reproducible. Principle
4 governs what is hashed; this governs what may reach the thing being hashed. A
search index, a documentation system or a language model may read design records
freely and may suggest anything to a person, but none may originate a parameter
that lands in a record. The failure is silent and compounding -- if suggestions
become records and records become the corpus, the corpus teaches itself its own
guesses, and every record remains perfectly correct about what it was built
from. Where a suggestion informed a build, the record's note field says so,
being already excluded from both hashes. No new field is needed and the
discipline is written down before there is anything to guard against.
Principle 18: a contribution is admitted by the oracle, not parsed by the
compiler. Author in whatever notation suits you, freeze the cases inside the
pinned toolchain, port, and admit when the oracle passes byte-identically. This
has happened twice, with both .scad generators -- it describes what was done
rather than what is planned. Two consequences: no second geometry engine is ever
kept in step with the first, and legacy/ holds the reference tier rather than
superseded code. When someone asks for another input format, the answer is that
notation never runs in production.
A third principle was drafted and dropped -- that artifacts cross a service
boundary and geometry does not. Section 4a's own rule is that use cases precede
interface proliferation, and writing a principle about a kernel service boundary
while the kernel is not a service is precisely that. It is a gate, not a
principle, and the kernel can acquire one when it acquires a boundary.
HANDOFF section 11 gains rows 12 through 16, all found by reading ROADMAP.md
properly rather than grepping it. Section 5 names a production FQDN that the
printed name contradicts. Section 5 says the production proxy is not on the
WireGuard side, for a reason the DNAT at 1fdb115 removed. Section 4a says no use
case crosses the Kane Fabric boundary and that neither project defines the
interface -- membership-gated export is that use case, and IDENTITY-CONTRACT.md
defines the identity half, while the siting interface 4a is actually about
remains undefined and should stay so. ROADMAP section 4 and HANDOFF section 4
are different lists. And legacy/ is misnamed.
None are resolved here. Two are CIVICVS's, two want the whole repository in view
at once, one is cosmetic. Recorded so the reconciliation pass finds them written
down rather than rediscovering them cold.
330 lines
16 KiB
Markdown
330 lines
16 KiB
Markdown
# ROADMAP.md
|
||
|
||
What the Mechanical Compiler is for.
|
||
|
||
**This document records intent, not commitment.** It is a reference for what the
|
||
project aims at and why; it does not schedule work and nothing here is a promise
|
||
that something will be built. `HANDOFF.md` §4 holds the actual order. Where the
|
||
two disagree, §4 wins on sequence and this document wins on purpose.
|
||
|
||
§§1, 2, 3 and 7 are the durable parts and are still correct. §4's sequence and
|
||
§5's production assumptions have both drifted since 18 AUG; the divergences are
|
||
recorded as open questions in `HANDOFF.md` §11 rather than patched here, because
|
||
resolving them needs the whole repository in view at once.
|
||
|
||
| | |
|
||
|---|---|
|
||
| Updated | 2026-08-18 |
|
||
| Companions | `PROCESS.md`, `ENVIRONMENT.md`, `STAGING-STATE.md`, `FAILURES.md` |
|
||
|
||
---
|
||
|
||
## 1. Mission
|
||
|
||
**Build the capacity to construct real structures from reclaimed and
|
||
commodity materials, using whatever fabrication a person actually has.**
|
||
|
||
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, from 3D printing, tabletop CNC, welding, cement casting, and
|
||
COTS stock — provided someone has worked out what the pieces are and how they
|
||
meet.
|
||
|
||
That last clause is the whole project. The Mechanical Compiler exists to make
|
||
the pieces computable, qualifiable, and reproducible by someone who was not
|
||
present when they were designed.
|
||
|
||
**Explicitly not in scope:** building codes, permitting, and jurisdictional
|
||
approval. Qualification and compliance are different animals. 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.
|
||
Plenty of legitimate contexts — agricultural, temporary, accessory, artistic —
|
||
never need the second thing.
|
||
|
||
The cheap hedge, taken deliberately: **record physical claims, never verdicts.**
|
||
Section modulus, moment of inertia, material provenance, process parameters.
|
||
Those cost nothing to capture, since the geometry layer computes most of them
|
||
anyway, and they are what any future argument would need. Recording a pass/fail
|
||
verdict would bake in a jurisdiction we do not want to be bound to.
|
||
|
||
Measure and attest. Never adjudicate.
|
||
|
||
---
|
||
|
||
## 2. Three artifact classes
|
||
|
||
Reading the reference structure back into components gives three kinds of
|
||
thing, only one of which currently exists.
|
||
|
||
### Members — prismatic, exists
|
||
|
||
Straight runs where panels meet along a fold line. Fixed cross-section, 10 to
|
||
100 feet, made from pallet-strap bundles in a printed enclosure.
|
||
|
||
This is the strap-beam work. Eleven profiles, locked at revision 8.0.0, frozen
|
||
as a 123-case fixture oracle.
|
||
|
||
Read as edge conditions rather than as shapes, the catalogue stops looking
|
||
arbitrary:
|
||
|
||
| Profile | Edge condition |
|
||
|---|---|
|
||
| T | panel-edge stiffener — flange takes the sheet, stem is the web |
|
||
| Y, Three-Fin | seams where three surfaces converge along a line |
|
||
| A Frame | a gable in section |
|
||
| Ring profiles | closed torsion boxes — perimeter, sill, deck edge |
|
||
|
||
The fins are not decoration. They are where the panel gets fastened.
|
||
|
||
### Nodes — non-prismatic, does not exist
|
||
|
||
Where fold lines converge. Look at the hipped ends of the reference structure:
|
||
four or five edges meeting at a vertex, at angles that differ at every corner.
|
||
|
||
Short, geometrically unique, almost certainly fully printed or cast. **Not a
|
||
strap beam, and will not come out of the current library.** No representation
|
||
exists yet.
|
||
|
||
### Panels — sheet, does not exist
|
||
|
||
The planar faces. CNC-cut from sheet goods. Every face in the reference
|
||
structure is planar, which is a hard constraint on the form and also exactly
|
||
what makes it buildable from sheet plus edge members.
|
||
|
||
Convenient technical note: planar polygon algebra, edge extraction and
|
||
unfolding are precisely what Shapely and CadQuery are good at. The toolchain
|
||
choice looks better after seeing the reference than before it.
|
||
|
||
**This is why interface contracts sit at the centre of the design.** A node
|
||
declares what member ends it accepts; a member declares what end it presents; a
|
||
panel declares its edges. Without that, three artifact classes are three
|
||
unrelated generators.
|
||
|
||
---
|
||
|
||
## 3. The gap that changes the backlog
|
||
|
||
If two panels meet at 137°, none of the eleven profiles gives you a member for
|
||
it.
|
||
|
||
`a_frame_leg_angle_deg` is the only dihedral-ish parameter anywhere, and the
|
||
crossbar geometry pins it to 20–53°. The Three-Fin's fins are locked at 120°
|
||
because they derive from a regular polygon. The library solves placement
|
||
against **webs** — the PLA between strap channels — but nothing solves
|
||
placement against **the angle between attachment faces**.
|
||
|
||
Closing this is an addition the architecture already supports:
|
||
`sb_member_on_edge` takes an arbitrary direction, `sb_fin_profile` takes N, and
|
||
the solver converges on measured quantities. A dihedral-parameterised family is
|
||
a new call into the same machinery, not a new machine.
|
||
|
||
**It moves ahead of the rebar-core bore variant in priority.**
|
||
|
||
---
|
||
|
||
## 4. Sequence
|
||
|
||
### Done
|
||
|
||
- **Rev 8.0.0 generators.** Six 3x profiles, five 4x profiles, shared N-generic
|
||
library. Every wall measures its declared minimum; symmetric profiles are
|
||
provably non-chiral; profile parameters are isolated from one another.
|
||
- **Fixture oracle frozen.** 123 cases, `ddd0f154…`, pinned to OpenSCAD 2021.01
|
||
and BOSL2 `92d697c`. The ten rejected cases are part of the contract.
|
||
- **Staging infrastructure accepted, 2026-08-16.** Host, both containers,
|
||
network isolation, bastion access, TLS trust, reverse proxy and the
|
||
application filesystem foundation are proven on `srv-b`. Mail alerting and
|
||
explicit disk monitoring are deferred; backup strategy is postponed by
|
||
operator decision; application deployment remains blocked on application
|
||
code. See `STAGING-STATE.md`.
|
||
- **Mail alerting accepted, 2026-08-17.** Delivered end to end and received
|
||
twice.
|
||
- **Disk monitoring accepted, 2026-08-17.** Four P410i members explicitly
|
||
monitored, four test alerts received, serial numbers recorded, reboot-proven.
|
||
- **Container SMTP egress blocked, 2026-08-17.** The project-local half of F-025
|
||
is corrected; the estate question about `wg-pk` client trust remains open.
|
||
- **Repository seeded, 2026-08-18.** Reference implementation, frozen oracle,
|
||
pinned toolchain and test harness at `c7e32d8`. Dependencies installed, oracle
|
||
verified in-container, `3 passed, 236 skipped`.
|
||
- **Container baseline established, 2026-08-18.** All three containers on
|
||
`srv-b` conform, asserted by an executable check rather than by inspection.
|
||
|
||
### Next — the port
|
||
|
||
Port `sb-geom` to Shapely; `pytest -n auto` green against all 123 cases. A port
|
||
that accepts the ten rejections is wrong.
|
||
|
||
Why this rather than more OpenSCAD: exceptions can be caught. Roughly half the
|
||
defensive complexity in the current library exists because an OpenSCAD assert
|
||
is fatal and uncatchable — `sb_path_max_round()` exists solely because a radius
|
||
could not be probed and the failure caught. Testing becomes pytest in CI rather
|
||
than bash scraping echo lines. And SVG generated from Python can emit the
|
||
outline, each strap channel and the bore as separately classed paths, which is
|
||
what makes hover and layer toggling possible in the catalogue.
|
||
|
||
### Then, roughly in order
|
||
|
||
1. **Dihedral-parameterised members** — see §3.
|
||
2. **Rebar-core bore variant** — Utility One's 40-ft side member is three strap
|
||
chords around a rebar core. The bore stops being a byproduct of the inside
|
||
walls and becomes a declared interface with a diameter and a fit class.
|
||
3. **Catalogue front end** — the reason for the web application. Customizer is
|
||
too limiting; SVG plus JavaScript, driven by the same generators.
|
||
4. **Node connectors** — new artifact class, new representation.
|
||
5. **Panels and unfolding** — new artifact class.
|
||
6. **Interface contracts** — formalised once two classes exist to connect. Not
|
||
before; §6 of Part I is right that use cases precede interface
|
||
proliferation.
|
||
7. **Qualification and attestation** — physical claims bound to a generator
|
||
revision. `SB_REVISION` already exists for this.
|
||
8. **Production instance** — automation derived from the proven staging
|
||
procedure. See §5.
|
||
9. **YunoHost packaging** — see §6.
|
||
|
||
Higher polygon counts are **not** on this list. 4x is the end of the N-strap
|
||
family; hexagons and above compose from triangles and squares rather than
|
||
getting their own generators.
|
||
|
||
---
|
||
|
||
## 4a. Parallel project
|
||
|
||
**Kane Fabric** — a separate project on the same host, and the platform on
|
||
which SASE, HOA Diagnostics, the Mechanical Compiler and other SASE-consuming
|
||
projects are implemented.
|
||
|
||
It is recorded here so its existence is known, not because it creates work.
|
||
Kane Fabric owns geographic state; the Mechanical Compiler owns member
|
||
geometry, qualification and workflow state. Both sets of documents describe a
|
||
future interface between them; **neither defines it, and that is deliberate.**
|
||
Part I §6 holds that use cases precede interface proliferation, and no use case
|
||
crosses the boundary yet.
|
||
|
||
The contract becomes real work when a compiled member first needs to be sited
|
||
somewhere. It will be cheaper to define then, against a concrete artifact.
|
||
|
||
---
|
||
|
||
## 5. Staging to production
|
||
|
||
`srv-b` is standalone and will remain so. Promotion is export/restore, not
|
||
cluster migration.
|
||
|
||
The order is deliberate: **manual first, then automation derived from the
|
||
proven procedure.** `FAILURES.md` is the evidence for why. Three specification
|
||
assumptions were wrong in ways only contact with a host revealed, and scripts
|
||
written from the specification alone would have faithfully reproduced all
|
||
three — one of them producing a container that booted, looked healthy, and had
|
||
four broken services.
|
||
|
||
When production is provisioned, the automation is written from `FAILURES.md`
|
||
first and `ENVIRONMENT.md` second.
|
||
|
||
Production differs from staging in four known ways, recorded so they are not
|
||
invented under pressure:
|
||
|
||
| | Staging | Production |
|
||
|---|---|---|
|
||
| Certificate | local staging CA | Let's Encrypt, per-name, no wildcard |
|
||
| FQDN | `mechanical-compiler.dev.infra` | `mechanical-compiler.manufacturing.kane-il.us` |
|
||
| Proxmox firewall | disabled | must be revisited |
|
||
| Proxy location | CT 101 on the same host | undecided — **not on the WireGuard side**, since the current NAT is outbound-only |
|
||
|
||
Three variables change in `mechcomp.env`: `MECHCOMP_ENV`, `MECHCOMP_BIND`,
|
||
`MECHCOMP_BASE_URL`. Nothing else.
|
||
|
||
---
|
||
|
||
## 6. Distribution
|
||
|
||
**YunoHost and Docker are parallel targets, not sequential.** YunoHost apps
|
||
install natively — apt, venv, systemd, nginx — and the project does not want
|
||
Docker inside YunoHost. Neither blocks the other, and neither should be built
|
||
"in order to" reach the other.
|
||
|
||
On the catalog's weight criterion: it reads "resource-hungry **compared to
|
||
their features**" and is aimed at marginal apps. `fab-manager_ynh` is in the
|
||
catalog; `paperless-ngx_ynh` declares nine apt dependencies including
|
||
postgresql and redis. The useful comparison is those, not a hello-world.
|
||
|
||
On provenance: the project is LLM-generated under human direction, stated
|
||
plainly in the README and in any submission. The catalog policy is a **quality
|
||
bar with a disclosure requirement, not a ban** — it rejects generated packages
|
||
that do not follow `example_ynh`, citing verbose code, hallucinated helpers and
|
||
non-standard directory architectures, then explicitly permits AI use where the
|
||
maintainer is transparent and can explain every line.
|
||
|
||
Two consequences. The package is drafted against `example_ynh` and reviewed by
|
||
CIVICVS as his own work; anything he would not defend in a review thread does
|
||
not ship. And disclosure belongs in the README, not in the pitch — if
|
||
provenance becomes the pitch, the project gets judged on that axis instead of
|
||
on whether it makes distributed manufacturing capacity legible.
|
||
|
||
---
|
||
|
||
## 7. Standing principles
|
||
|
||
1. **Measure, do not assume.** Every wall thickness in rev 8 is a measured
|
||
value. Connectivity is necessary and never sufficient — revision 7 passed
|
||
cross-sections joined by 0.13 mm.
|
||
2. **Solve numerically against the real quantity.** Closed-form placement is
|
||
where the wrong-by-a-cosine errors came from.
|
||
3. **Structure before decoration.** Members butt through their neighbours;
|
||
fillets are applied on top of that overlap, never instead of it.
|
||
4. **Hash inputs, not outputs.** Mesh bytes are not reproducible across
|
||
toolchain versions. Hashing parameters plus generator revision keeps a
|
||
qualification valid across an upgrade that did not change the geometry.
|
||
5. **A permanent deviation is not a deviation — it is the specification.**
|
||
Promote it and delete the exception.
|
||
6. **Record the failure before correcting it.** Then apply the smallest
|
||
corrective change, not the one that also fixes three things you were
|
||
worried about.
|
||
7. **Prove the negative.** Isolation is asserted by showing the forbidden path
|
||
fails, never by showing the interface is gone (F-018). Health is asserted
|
||
after a reboot, never before (F-021).
|
||
8. **Handoff is not delivery.** An upstream acceptance code proves the message
|
||
left, not that it arrived (F-023). The same distinction applies wherever a
|
||
subsystem reports success on behalf of something downstream.
|
||
9. **Record what a change grants, not only what it repairs.** A trust rule
|
||
added to fix one rejection may authorise far more than the case that
|
||
prompted it (F-025).
|
||
10. **Shared infrastructure is not ours to reconfigure.** An instance may
|
||
configure its own client side; changes further down the chain are escalated
|
||
with evidence.
|
||
11. **A test must distinguish tool failure from the condition it tests.** One
|
||
whose failure mode is indistinguishable from success manufactures
|
||
confidence (F-027). Every negative assertion first establishes that it could
|
||
have observed the positive case.
|
||
12. **A component surviving its own restart is not proven to survive the
|
||
host's.** Different machinery; assert against the one that matters (F-026).
|
||
13. **A standard must be executable.** If conformance cannot be asserted by
|
||
running something, it is not a standard but a recollection, and it will
|
||
drift. A property not checked is not part of the standard (F-031).
|
||
14. **Check the thing, not something related to it.** An assertion inferred
|
||
from an adjacent observation is not evidence, however reasonable the
|
||
inference (F-031).
|
||
15. **Verify by counting, not by scanning.** A long, correct-looking list is
|
||
exactly where an unexpected 437 files hide (F-029).
|
||
16. **Conformance precedes backup.** Backing up an unverified configuration
|
||
preserves the confusion along with the data, and a restore returns it
|
||
faithfully.
|
||
17. **Nothing approximate may write to anything reproducible.** Principle 4
|
||
governs what is hashed; this governs what may reach the thing being hashed.
|
||
A search index, a documentation system or a language model may read design
|
||
records freely and may suggest anything to a person. None of them may
|
||
originate a parameter that lands in a record. The failure is silent and
|
||
compounding: if suggestions become records and records become the corpus,
|
||
the corpus teaches itself its own guesses, and every record is still
|
||
perfectly correct about what it was built from. A suggestion is an input to
|
||
a person, never an input to a build — and where one informed a build, the
|
||
record's `note` field says so, being already excluded from both hashes.
|
||
18. **A contribution is admitted by the oracle, not parsed by the compiler.**
|
||
The path from a new profile to the catalogue runs: author it in whatever
|
||
notation suits you, freeze its cases inside the pinned toolchain, port it,
|
||
and admit it when the oracle passes byte-identically. This has happened
|
||
twice, with both `.scad` generators. The consequence is that no second
|
||
geometry engine is ever kept in step with the first, and `legacy/` holds the
|
||
reference tier rather than superseded code. When someone asks for another
|
||
input format, the answer is that notation never runs in production — only
|
||
the kernel does.
|