diff --git a/docs/FAILURES.md b/docs/FAILURES.md index 17a44eb..d7265e0 100644 --- a/docs/FAILURES.md +++ b/docs/FAILURES.md @@ -6,7 +6,7 @@ Compiler environment. | | | |---|---| | Scope | All instances. Staging entries are marked `srv-b`. | -| Updated | 2026-08-22, closing F-034 | +| Updated | 2026-09-12, recording F-037 | | Method | `PROCESS.md` section 7 | | Rule | Append only. Never edit an entry except to add a `Resolution` line. | | Numbering | Sequential, never reused. See §0 on the renumbering. | @@ -1022,6 +1022,56 @@ that exist only as examples; each is a future F-035. --- +### F-037 — a tar stream rooted at `.` re-owned the repository root + +CT 100. Development delivery. + +**Observed:** after landing a five-file tarball and running `chown -R +mechcomp:mechcomp` on `src` and `tests`, every subsequent git command failed +with `fatal: detected dubious ownership in repository at '/var/www/mechcomp'`, +and git offered `git config --global --add safe.directory /var/www/mechcomp` as +the remedy. `stat` showed `/var/www/mechcomp` at `root:root`, while `.git`, +`src`, `tests` and `Makefile` were all still `mechcomp:mechcomp`. The test suite +ran green throughout — 547 passed — because pytest does not care who owns the +directory. + +**Cause:** proven. The delivery used `tar cf - -C /root/incoming/unpack . | pct +exec 100 -- tar xf - -C /var/www/mechcomp`. A tar stream rooted at `.` carries +an entry for the destination directory itself; `tar x` ran as root and applied +that entry's ownership to `/var/www/mechcomp`. The `chown` that followed named +only the payload subdirectories, so nothing corrected the root. Everything +*below* the root was already correct, which is why the damage was invisible to +every check except git's own. + +**Correction:** `chown mechcomp:mechcomp /var/www/mechcomp` — one path, owner +only, no `-R`. Mode was already 755 and unchanged. `-R` was deliberately not +used: everything underneath was already correct, and reaching for it is the +habit behind F-007. `safe.directory` was **not** added; that is F-008 and would +have masked this and every later instance. + +**Consequence:** three, and two of them are mine rather than the environment's. + +1. `PROCESS.md` §3 gains two REQs: the `chown` must name the directory the + files land *in*, and a tar transport must name its top-level directories + rather than being rooted at `.`. The delivery path as documented produces + this every time, so an automation writer following §3 alone lands here. +2. The verification step ran *before* the landing that destroyed it. A `git + status` placed ahead of the breaking step reports a clean tree and reads as + a pass. Verification must follow the step it verifies — the F-027 pattern + again, in a new place. +3. §7 says record a failure before correcting it. I corrected first and wrote + this afterwards. Recorded as a process defect rather than quietly fixed, + because an undisclosed one teaches the next assistant that the rule is + optional. + +**Open:** `ct-baseline.sh` exits 0 with this condition present — it does not +check the ownership of a service's working tree, so by §9a the property is not +part of the standard. Whether it should be is a CIVICVS decision; the script is +host property covering three projects. Carried as open question 11 in +`HANDOFF.md`. + +--- + ## Open, not closed | # | Status | @@ -1045,5 +1095,6 @@ that exist only as examples; each is a future F-035. | F-034 | **Closed** 2026-08-22. Measured: the port is exact, the reference is noisy. 30 of 113 accepted cases, worst relative error 2.24e-05, confined to `SECTION_AREA_MM2` and its two derivatives. Nothing correctable in the port; resolved in `test_oracle.py` by bounding at eight units in the last place of the oracle's six-significant-figure record. Suite green at 468 passed. Specification in `docs/ACCEPTANCE.md`. | | F-035 | **Corrected** 2026-08-19. Use `runuser`, never `su`; `mechcomp` is `nologin`. | | F-036 | **Corrected** 2026-08-22. The interpreter is `venv/bin/python`, never system `python3`. Same class as F-035. | +| F-037 | **Corrected** 2026-09-12. Owner of the repository root restored; `PROCESS.md` §3 amended. `safe.directory` not used. Baseline coverage open — see open question 11. | Everything else is closed with a proven cause and a proven correction. diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index c7279ff..e68358d 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -6,7 +6,7 @@ It is the only handoff you need to read. Dated handoffs in `docs/archive/` are historical and are not required reading — do not diff them against this to work out what is true. If something here is wrong, correct it here. -Last updated 2026-09-11, after the composer went live behind the proxy. +Last updated 2026-09-12, after the authorship field landed. This line used to carry the commit hash of its own rewrite. It cannot: the hash is not known until the commit is made, so the value was always the @@ -169,9 +169,9 @@ terminate rather than recur. ### The port — COMPLETE -Gitea `main` at `a8081e1` before this commit. CT 100 clean and matching. +Gitea `main` at `48d5665` before this commit. CT 100 clean and matching. -**Suite: 529 passed, 0 failed.** The 30 expected failures are gone, resolved +**Suite: 547 passed, 0 failed.** The 30 expected failures are gone, resolved rather than suppressed, by the tolerance model in `docs/ACCEPTANCE.md`. A red `make test` now means something is actually wrong. @@ -194,7 +194,7 @@ rather than suppressed, by the tolerance model in `docs/ACCEPTANCE.md`. A red | `profiles/three_x.py` | `strap-beam-3x.scad` | Six profiles, defaults, 3x base checks | | `profiles/__init__.py` | — | `build()`, `ProfileRejected`, family registry | | `stock.py` | — | COTS stock descriptor: `RectStock`, `RoundStock`, `Fit`, `Provenance`. A leaf module — imports nothing from `mechcomp`. See `docs/STOCK.md` | -| `design_record.py` | — | What a model was made from, at full precision, sufficient to regenerate it. `input_id` and `build_id` | +| `design_record.py` | — | What a model was made from, at full precision, sufficient to regenerate it. `input_id` and `build_id`, and `Author` — recorded, rendered with its own limit, and in neither hash | | `svg.py` | — | Cross-section as SVG, separately classed layers for material, cavities and stock | | `web/app.py` | — | The composer. Standard library HTTP server; binding from `/etc/mechcomp/mechcomp.env` | @@ -220,20 +220,39 @@ deliverable he wants. Two consequences: after export. `ROADMAP.md` §4 orders it third; that ordering predates this and should be read against it. -**Priority order, decided 11 SEP when CIVICVS delegated it.** The reasoning is -given so a successor can disagree with the argument rather than only the -sequence. +**Priority order, decided 11 SEP when CIVICVS delegated it. Items 1 and 2 were +reversed on 12 SEP, for the reason given under item 1.** The reasoning is given +so a successor can disagree with the argument rather than only the sequence. -1. **STL export.** No new dependency, no kernel, and it is the only item that +1. **An authorship field in the design record. Landed at `48d5665`.** One field, + and a door that narrows rather than closes. The original argument was that + records created before it exists can never be attributed. That was too + strong, and it was too strong *because* the field is excluded from both + hashes: a record regenerated later with the author filled in keeps its + `input_id`. But `build_id` covers the code revision and the Shapely and GEOS + versions, and boolean results on near-degenerate geometry can shift between + GEOS releases — the F-034 mechanism. Regenerate after a GEOS bump and you + have attribution under a different build identity, beside a printed part + nobody can now tie to either. Recoverable, not free. It still went first, + because it is one field and one parser branch, and doing it first means the + first coupon off the printer carries its author. + + The person is identified by an email address. A handle may be chosen later + and does not replace it. Nothing verifies the address and the record says so + on the line it appears on. +2. **STL export.** No new dependency, no kernel, and it is the only item that produces *physical* feedback. Everything so far is verified against a frozen oracle; a printed coupon is the first test against reality, and the first real measurement of whether `fit_clearance_mm` suits the operator's printer. The composer already makes stock, fit and walls configurable, which was his stated precondition for printing anything. -2. **An authorship field in the design record.** One field, and a one-way door. - Records created before it exists can never be attributed. Cheap now, - impossible retroactively — and a multi-user system needs the record to answer - *who* as well as *what*. + + **`length_view` is not in `COMMON_GROUPS`.** The Full Length branch is + therefore unreachable from the composer, so every export would silently be a + 100 mm preview — a file that looks right, slices right, and is the wrong + object. The sweep must be `model_length_mm(p)`, the value already published + as `LENGTH_MM`, or the record's `VOLUME_MM3` and `MASS_G` describe something + other than the file beside them. That control has to exist first. 3. **Per-member stock — the conduit core.** What CIVICVS asked for on 22 AUG and still cannot be done: `Geo` is global to a build, so every member is the same rectangular strap by construction. Reaches into `join.py` and @@ -268,16 +287,26 @@ Roadmap items — read `ROADMAP.md` before starting any: anything, persist anything, or show a bill of materials. `payload["defaults"]` is sent to the browser and nothing reads it — dead weight, and it is what made a verification grep lie on 11 SEP. -- STL and STEP export. No CAD kernel is installed in CT 100 (§5). +- STEP export. Needs a CAD kernel and none is installed in CT 100 (§5). + STL needs none — a member is prismatic by definition. See §5. - `mechcomp-worker.service`. `src/mechcomp/worker/` is still a 36-byte stub and nothing needs a worker yet. - Nodes (non-prismatic) and panels (sheet). No representation exists for either. -**`WORK-ORDER-004` publishes the composer at `dev.mechcomp.kane-il.us`.** It is -infrastructure-mode work on `wg-pk`, which this project does not own, so it is -an escalation under `PROCESS.md` §7 and not something to drive with command -groups. The `srv-b` half needs no change; the single gate is `AllowedIPs` on the -hub's peer entry. **Until it closes, CIVICVS cannot see any of this work.** +**`WORK-ORDER-004` is closed. The composer is public at +`dev.mechcomp.kane-il.us`.** Executed 11 SEP at `1fdb115`: browser, DNS, TLS on +`wg-pk`, the WireGuard tunnel, a DNAT on `srv-b` scoped to the hub as source, +CT 100. No WireGuard change and no route were made — the hub's peer entry for +`srv-b` is still a `/32`, as all twenty are — because every vhost there proxies +to a tunnel address directly and following that convention removed the only step +that could have locked the operator out of `srv-b`. + +An earlier version of this paragraph said the work was pending and that CIVICVS +could not see any of it. It was written thirty-two minutes after the ingress +closed, in the same session, and was wrong the moment it was committed: the §4 +rewrite was made against the work order's old state rather than its new one. +Corrected 12 SEP. This is the staleness §0 exists to prevent, and it happened +anyway, inside one session. Note for anyone tempted to shortcut a test print: the OpenSCAD reference in `legacy/` does export printable STL today — `sb_extrude_section` does a @@ -521,6 +550,13 @@ confirm the tests notice. This found real gaps in five of six slices. It also caught a malformed mutation of mine — cutting the cavities twice is idempotent. **A surviving mutation is sometimes a bad mutation, not a test gap.** +**Set `PYTHONDONTWRITEBYTECODE=1` and clear `__pycache__` between mutations.** A +stale `.pyc` once masked a real defect, and every mutation result reported +before that was caught was optimistic by an unknown amount. A mutation that +survives because the test ran old bytecode is indistinguishable from one that +survives because the test is weak — F-027 in different clothes, and the reason +that rule is general rather than about one harness. + **Read-only before write.** Every command group where the answer was not certain established the facts first. @@ -597,7 +633,11 @@ held to it. When in doubt, narrow. | 4 | Kane Fabric participant mail, send and receive | Cross-project | | 5 | ~~Tolerance model for the three discretisation-limited keys~~ | **Closed 22 AUG.** See §7 and `docs/ACCEPTANCE.md` | | 6 | Is the bore's fit class per-material, or one clearance for all inserts? | CIVICVS — raised by the conduit-core requirement, §4 | -| 7 | Public ingress at `dev.mechcomp.kane-il.us` | `WORK-ORDER-004`; CIVICVS executes on `wg-pk` | +| 7 | ~~Public ingress at `dev.mechcomp.kane-il.us`~~ | **Closed 11 SEP** at `1fdb115`. Executed without the WireGuard change the work order proposed | +| 8 | TLS renewal has never been observed to succeed for this name; first due before 2026-12-10 | CIVICVS | +| 9 | The composer has no acceptance criteria of its own — only the path to it does | Architect | +| 10 | The service is world-reachable and unauthenticated | CIVICVS; §4 item 5 makes this due rather than hypothetical now that it is published | +| 11 | Should `ct-baseline.sh` check the ownership of a service's working tree? | CIVICVS — host property, raised by F-037 | Question 4 is real and unaddressed. Containers do not send mail by standard and nothing can reach `vmbr1` from outside, so receiving has no path at all. It needs diff --git a/docs/PROCESS.md b/docs/PROCESS.md index 46e8026..eff6669 100644 --- a/docs/PROCESS.md +++ b/docs/PROCESS.md @@ -130,6 +130,18 @@ always the same, so nothing has to be remembered between sessions. repository operations run as the service user, and a root-owned file inside the tree causes exactly that failure later. +**REQ (F-037)** — The `chown` must name the directory the files landed *in*, not +only the files. A tar stream rooted at `.` carries an entry for the destination +directory itself, and `tar x` as root rewrites that directory's ownership. +Naming only the payload subdirectories leaves the repository root `root:root`, +and git then refuses the worktree with the F-008 message — which invites the +F-008 mistake as its own remedy. Fix the owner. Never add `safe.directory`. + +**REQ (F-037)** — Verification that depends on the repository being intact must +run *after* the landing, and a landing step must not be able to destroy the +check that would have caught it. A `git diff` placed before the step that broke +git reports nothing wrong and looks like a pass. + **REQ** — An assistant delivering files states, in this order: what the archive contains, where it expands, what it overwrites, and how to verify it landed correctly. "Overwrites nothing" is a claim that must be checked, not assumed. @@ -139,6 +151,11 @@ correctly. "Overwrites nothing" is a claim that must be checked, not assumed. Prefer one tarball that expands over the existing tree. Individual file paths are error-prone to transcribe through a file manager. +Name the payload's top-level directories explicitly, when creating the archive +and when extracting it: `tar cf - -C