← Commit history

docs: 0.7.4–0.7.6 — per-type floors, unknown kind never defaults to part, runtime naming fallback, per-file reopen hints; 28 adversary passes

Drew Owens ·ff631c05b7 ·1mo ago ·parent 2b43d4c
4 files changed +70−8
README.md+49−3
@@ -312,8 +312,9 @@ Before any of that, the sweep asks whether it **fits**. Marker and floor catch a empty file and a foreign one; neither catches *the right file, cut short* — the marker sits at offset 4, so every prefix of a real document carries it, and a truncation past 16 KB clears the floor. The format offers nothing cheap that-would catch it. So the sweep estimates what the session will write (on-disk sizes-for documents it can measure, an upward estimate for the rest) and **refuses to+would catch it. So the sweep estimates what the session will write — on-disk sizes for documents+it can measure, and for the rest a floor that depends on the document's KIND,+because an assembly is about thirty times the median part — and **refuses to begin** when the recovery volume has less than twice that free: `insufficient_headroom`, naming free bytes, needed bytes and the shortfall, with nothing written and nothing closed. It asks again before *every* save, because@@ -433,7 +434,52 @@ not.  ## Roadmap -- v0.7.3 (this): **it refuses to start a sweep it cannot fit.** Pass 24 found the+- v0.7.6 (this): **ask the evidence already in hand, and tell the user how to+  reopen what was rescued.** When `GetType` fails, the document's OWN PATH is now+  consulted before its title — the path is the user's file as SOLIDWORKS named it,+  while a title often has no extension at all (Explorer hides known types by+  default). That narrows the neutral `.sldrecover` name to the only case with no+  extension anywhere: a pathless document whose type could not be read. Asking the+  path is not the circularity 0.7.5 removed — the path describes the document; the+  recovery file's name is chosen by the sweep FROM the kind, so reading a kind out+  of THAT can only repeat an assumption. If SOLIDWORKS refuses to write the+  neutral name (`SaveAs3` dispatches its writer on the extension, and this bridge+  does not assume what it does with one it has never seen), the document is+  re-saved as `.SLDPRT` and the record says `unknownKindNamedAsPart` — the+  verification does NOT soften, because the kind is still unknown and the name is+  only a label. And `swx_recover_list` now gives every file its own `howToOpen`: a+  `.sldrecover` file says rename it and to what, rather than "reopen with+  swx_open", which is the one instruction that cannot work for it. 341 tests.+- v0.7.5: **the classifier can say "I do not know".** Pass 26 found a+  data-loss path that only 0.7.4's per-kind floors made reachable, and it is+  circular: `GetType` failing on a document being closed under memory pressure —+  the condition the sweep exists for — became "part" via `unwrap_or(1)`; the+  recovery file was then NAMED `.SLDPRT` from that; and the verification read the+  kind back out of the name it had just produced. A derived value cannot check the+  thing it was derived from, and a 30 KB truncated assembly cleared the 24 KB part+  floor (the smallest real assembly measured here is 95,492 bytes). The kind is now+  carried from the COM read all the way to the verdict: an unreadable type is+  UNKNOWN, an unknown gets a neutral `.sldrecover` name rather than a guessed one,+  and it is verified against the strictest floor plus — when the document had a+  file of its own — at least what that file weighed. Classification is by file+  EXTENSION only, so a recovery folder called `…\Drawings\` no longer reclassifies+  every part under it as a drawing. A title is read as a title: `Assem1` is an+  assembly, not a part. Refusing to close is the safe error, and the doc comment+  that argued the opposite is gone. 336 tests.+- v0.7.4: **sizes by kind, because the three kinds are not the same size.**+  The 0.7.3 estimator costed a document it could not measure at twice the largest+  it could, which is not monotone: one measurable 29 KB part beside a pathless+  assembly LOWERED that assembly's assumed cost from 1 MB to 58 KB — learning+  something small about a session made the guess about the rest of it smaller, and+  that same number gates the check before every save. An unknown document now+  costs at least a floor set by its kind (part 256 KB, assembly 8 MB, drawing+  4 MB, each above the measured median), so a measured neighbour can never lower+  it. The recovery floor splits by kind too — part 24 KB, assembly 80 KB, drawing+  256 KB, each just under the smallest real document of its kind measured here+  (29,130 / 95,492 / 319,803) — because one floor for all three has to be the+  lowest of them, which would let an assembly stop at a third of its size and+  still pass. 330 tests.+- v0.7.3: **it refuses to start a sweep it cannot fit.** Pass 24 found the   gap the 0.7.2 checks structurally cannot close: the container marker lives at   offset 4, so ANY prefix of a real document carries it, and a write that reaches   20 KB of a 500 KB assembly clears the 16 KB floor too. Marker and floor catch
SKILL.md+11−3
@@ -30,7 +30,8 @@ adom-bridge --ai-thread "<your thread>" swx_bom '{"path":"C:\\designs\\gearbox.s   then edited is copied into recovery under a new name; their file on disk is   untouched and their unsaved changes still survive. - **It refuses to start a sweep that will not fit.** Before writing anything it-  estimates the session and compares it with free space on the recovery volume;+  estimates the session — by document kind, since an assembly is about thirty+  times the median part — and compares it with free space on the recovery volume;   if there is less than twice the estimate it answers `insufficient_headroom`   with the numbers, having written and closed NOTHING. Free some space, or point   `recoveryDir` at a volume with room. It re-checks before every save, so a disk@@ -39,7 +40,10 @@ adom-bridge --ai-thread "<your thread>" swx_bom '{"path":"C:\\designs\\gearbox.s   reply. The reason is a truncation nobody can detect afterwards: a recovery file   can be the RIGHT document CUT SHORT and nothing in the file would show it. - **"On disk" is checked structurally.** Each recovery file must LOOK like a-  SOLIDWORKS document (the container marker at its head) and be at least 16 KB;+  SOLIDWORKS document (the container marker at its head) and be at least as big as+  the smallest real document of its kind (part 24 KB, assembly 80 KB, drawing+  256 KB — and if the bridge could not establish the kind, the strictest of the+  three plus at least the size of the document's own previous file);   both are reported per document as `recoverySignatureOk` and `recoveryBytes`. A   full disk leaves zero-byte and truncated files that `exists()` and any byte   floor accept; those are not saves, and the document stays open. The check fails@@ -60,7 +64,11 @@ adom-bridge --ai-thread "<your thread>" swx_bom '{"path":"C:\\designs\\gearbox.s - **`swx_close_all_safely {reason}`** is the same sweep without the quit, for when   the user wants an empty session rather than no SOLIDWORKS. - **`swx_recover_list`** shows what every sweep preserved and where, newest first.-  Tell the user this path when you close their work; `swx_open` reopens any of it.+  Tell the user this path when you close their work. FOLLOW EACH FILE'S OWN+  `howToOpen`: most say `swx_open`, but a file named `.sldrecover` is the rescue of+  a document whose kind could not be established, and SOLIDWORKS will not open it+  until it is renamed to `.SLDPRT`, `.SLDASM` or `.SLDDRW`. Never tell the user to+  `swx_open` one of those. - **`swx_shutdown` is idempotent.** SOLIDWORKS not running answers `success:true,   alreadyStopped:true`. It is the last thing you do, and the last thing should be   safe to repeat.
docs/ECOSYSTEM-TABLE.md+1−1
@@ -111,7 +111,7 @@ templates**, alisamsam **22**. | 43 | **Import / resource lifecycle — no leaked temps, drainable, enumeration never wedges** | ✗ | **✓ 0.5.0** — enumeration degrades above a cap (`{count,truncated,sample}`, never a 30s timeout+kill); `swx_close_import_temps` drains STEP/IGES `…\Temp\swx<pid>\IC~~\…` temps in the DEFAULT posture (path is the ownership proof, survives a pid change); import asserts a net +1. Root-caused a live 1342-doc / 8 GB leak. **0.5.3**: the resolved `%TEMP%` is the WHOLE location proof (8.3-aware, no shape-test fallback), and with no resolved root the drain refuses (`temp_root_unknown`) instead of closing nothing quietly. **0.5.4**: the report says which root it compared against (`tempRoot`) and counts documents that carry the launcher's tail but sit elsewhere (`importTempsElsewhere` + a sample), so a drain that matched nothing can no longer claim it drained everything. **0.5.6**: the victim is CLOSED by the path it was chosen by (it was closed by TITLE, which `CloseDoc` resolves by name without prompting — a live session held temps sharing a saved file's title), a shared title is refused and reported, and every close is verified by path — the mismatch case is real, because ab's `%TEMP%` need not be the one SOLIDWORKS used | ✗ | ✗ | ✗ | ✗ | ✗ | | 44 | **Graceful QUIT that is measured, not assumed** (never a kill, never answers a dialog, proves the exit from the process census) | ✗ | **⚠ 0.5.3 mock-proven, UNPROVEN LIVE** — a rung ladder (a `ExitApp` → f Restart Manager *without* `RmForceShutdown`), pinned at rung (a); 7 re-measured preconditions where UNKNOWN refuses and `force` waives the UNSAVED question ONLY; every COM reference released before and after each rung, with outstanding refs named in the report; success = *this pid AND this creation time* absent from the census, never `GetActiveObject`; per-session children reaped while the documented platform daemons are left alone; a dialog stops the ladder permanently and is never answered; nothing disowned until the exit is verified. every precondition re-measured before EVERY rung (a mid-ladder refusal is a real outcome), the exit POLLED to the rung's budget rather than sampled once (a real `ExitApp` returns before the process goes), the dialog census covering the `swCefSubProc` web-view children by parentage, the count cross-checked against the enumeration, and the message rungs re-verifying window ownership before they send. `tools/quit-gate.py` proves the session was bridge-launched and has not been run | ✗ | ✗ | ✗ | ✗ | ✗ | -| 45 | **Closes its own documents without asking — and loses nothing doing it** (preserve-then-close: every open document written to a recovery folder and verified there BEFORE anything closes) | ✗ | **⚠ 0.7.3 mock-proven, first live close PENDING** — `swx_shutdown {reason}` / `swx_close_all_safely {reason}` save every open document into `…\exports\recovery\<timestamp>\`, verify it, close parents before children, then quit; no write opt-in, no per-document confirmation (Drew, 2026-09-11: *"I despise every time you ask for help closing"*). The authorization is to CLOSE, not to lose anything, so the consent question is replaced by checkable rules: the sweep REFUSES TO START when the recovery volume cannot hold the session it is about to write (`insufficient_headroom` with free/needed/shortfall, nothing written, nothing closed), re-checked before every save, with `headroomBytesBefore` and `estimatedWriteBytes` always reported — because a recovery file can be the RIGHT document CUT SHORT and no check on the file can tell (the marker is at offset 4, so every prefix carries it); the recovery file is verified STRUCTURALLY — it must be a recognised SOLIDWORKS container AND at least 16 KB, published as `recoverySignatureOk` + `recoveryBytes`, and it fails CLOSED (`exists()` passes a zero-byte file and any byte floor passes a truncated one; both are what a full disk leaves). The container marker was MEASURED on 166 real documents from three hardware repositories, because the signature every reference names for a SOLIDWORKS document (OLE compound file) appears in NONE of them — verifying that, as pass 23 specified, would have refused to close every document on a modern install; a document that could not be written is left OPEN and named in `remaining`; the user's saved-then-edited file is copied to recovery under a new name and their original is asserted byte-for-byte unchanged; `SaveAs3` uses `swSaveAsOptions_Silent` ALONE so the document RE-POINTS at the recovery file and the close key is read from the document after the save; a dialog stops the sweep and is never answered, with the census taken before EVERY save and EVERY close (a save can raise a prompt of its own, which a per-batch census could not see until the closes had happened); and an UNREADABLE window census is its own outcome — saves proceed, nothing closes, `stoppedBy:"dialogCensusUnknown"`. Ordering is proven from a journal, not from the end state. Every rule is mutation-verified (disable it and exactly one test fails). **No live run yet** — first live use is `swx_close_all_safely` alone | ✗ | ✗ | ✗ | ✗ | ✗ |+| 45 | **Closes its own documents without asking — and loses nothing doing it** (preserve-then-close: every open document written to a recovery folder and verified there BEFORE anything closes) | ✗ | **⚠ 0.7.6 mock-proven, first live close PENDING** — `swx_shutdown {reason}` / `swx_close_all_safely {reason}` save every open document into `…\exports\recovery\<timestamp>\`, verify it, close parents before children, then quit; no write opt-in, no per-document confirmation (Drew, 2026-09-11: *"I despise every time you ask for help closing"*). The authorization is to CLOSE, not to lose anything, so the consent question is replaced by checkable rules: the sweep REFUSES TO START when the recovery volume cannot hold the session it is about to write (`insufficient_headroom` with free/needed/shortfall, nothing written, nothing closed), re-checked before every save, with `headroomBytesBefore` and `estimatedWriteBytes` always reported — because a recovery file can be the RIGHT document CUT SHORT and no check on the file can tell (the marker is at offset 4, so every prefix carries it); the recovery file is verified STRUCTURALLY — it must be a recognised SOLIDWORKS container AND at least as big as the smallest real document of its KIND (part 24 KB, assembly 80 KB, drawing 256 KB — one floor for all three would have to be the lowest of them, letting an assembly stop at a third of its size), with the KIND carried from the COM read to the verdict and NEVER re-derived from the recovery file's name, because that name is chosen from the kind and so cannot check it; an unreadable document type falls back to the document's OWN PATH and then its title (evidence about the document, not names this sweep chose) and only then to UNKNOWN, which takes a neutral `.sldrecover` name and the strictest floor — and if SOLIDWORKS refuses to write that name the file is re-saved as `.SLDPRT` with the fallback RECORDED and the unknown-kind verification unchanged; `swx_recover_list` gives every rescue its own `howToOpen`, because SOLIDWORKS will not open a `.sldrecover` file until it is renamed, published as `recoverySignatureOk` + `recoveryBytes`, and it fails CLOSED (`exists()` passes a zero-byte file and any byte floor passes a truncated one; both are what a full disk leaves). The container marker was MEASURED on 166 real documents from three hardware repositories, because the signature every reference names for a SOLIDWORKS document (OLE compound file) appears in NONE of them — verifying that, as pass 23 specified, would have refused to close every document on a modern install; a document that could not be written is left OPEN and named in `remaining`; the user's saved-then-edited file is copied to recovery under a new name and their original is asserted byte-for-byte unchanged; `SaveAs3` uses `swSaveAsOptions_Silent` ALONE so the document RE-POINTS at the recovery file and the close key is read from the document after the save; a dialog stops the sweep and is never answered, with the census taken before EVERY save and EVERY close (a save can raise a prompt of its own, which a per-batch census could not see until the closes had happened); and an UNREADABLE window census is its own outcome — saves proceed, nothing closes, `stoppedBy:"dialogCensusUnknown"`. Ordering is proven from a journal, not from the end state. Every rule is mutation-verified (disable it and exactly one test fails). **No live run yet** — first live use is `swx_close_all_safely` alone | ✗ | ✗ | ✗ | ✗ | ✗ |  *(45 numbered rows; rows 29–31 are the N/A block that accounts for most of the Fusion verb-count gap. Rows 36–42 added 2026-09-08 from the crate build and the launch-resilience rounds; rows 43–45 from the import-leak, quit and safe-close work — the bar is "significantly more powerful than the MCP servers", so every robustness capability gets a row with its evidence level, not a slogan.)* 
docs/QUIT-INVESTIGATION.md+9−1
@@ -1705,7 +1705,15 @@ carries its own rules, each one checkable and each one mutation-verified:   bridge could not detect afterwards is one it declines to cause. - **Nothing closes before it is on disk**, verified per document, and "on disk" is   a STRUCTURAL question: the recovery file must be a recognised SOLIDWORKS-  container and at least 16 KB (`recoverySignatureOk` and `recoveryBytes` in the+  container and at least as big as the smallest real document of its KIND — part+  24 KB, assembly 80 KB, drawing 256 KB, and the strictest of those (plus the+  original file's size) when the kind could not be established. The kind is+  carried from the COM read to the verdict, never re-derived from the recovery+  file's name: that name is CHOSEN from the kind, so reading it back checks+  nothing. When `GetType` fails the document's own PATH is consulted, then its+  title — both describe the document rather than repeating this sweep's own+  assumption — and only a pathless document with no extension anywhere is written+  under the neutral `.sldrecover` name (`recoverySignatureOk` and `recoveryBytes` in the   reply). `exists()` accepts a zero-byte file and a byte floor accepts a truncated   one; both are what a full disk leaves behind. The check fails CLOSED.   *Measured, 2026-09-11: of 166 real documents from three hardware repositories,