From aefbd94dfbf31121e5831ad21b8cf6e54ff9fccc Mon Sep 17 00:00:00 2001
From: Ikel
Date: Tue, 14 Jul 2026 14:02:22 +0800
Subject: [PATCH 1/2] feat: protect project revisions before overwrite
---
README.md | 7 +-
docs/FS-118_RELEASE_NOTES.md | 45 ++
docs/RELEASE_HYGIENE.md | 3 +
v3/dxf-export.js | 8 +-
v3/index.html | 579 +++++++++++++++++++---
v3/persistence/project-revisions.js | 351 +++++++++++++
v3/release-manifest.json | 9 +-
v3/tools/check-fs.js | 326 +++++++++++-
v3/tools/fixtures/legacy-v2.8-2floor.fstr | 84 ++++
9 files changed, 1301 insertions(+), 111 deletions(-)
create mode 100644 docs/FS-118_RELEASE_NOTES.md
create mode 100644 v3/persistence/project-revisions.js
create mode 100644 v3/tools/fixtures/legacy-v2.8-2floor.fstr
diff --git a/README.md b/README.md
index 9f36568..27f50ee 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ FutolStructure is a browser-based structural engineering workbench for reinforce
Security
-[](https://futolstructure.vercel.app)
+[](https://futolstructure.vercel.app)
[](https://github.com/michaelfutol/futolstructure/actions/workflows/validate.yml)


@@ -53,7 +53,7 @@ flowchart LR
- Tributary area and gravity load distribution from slabs to beams, columns, footings, and base reactions.
- Plan drafting aids including coordinated member tags, grid bubbles, dimensions, ortho measurement, snapping, and lock indicators.
- 3D review with display schemes, member colors and opacity controls, foundation geometry, slab transparency, and stair geometry.
-- `.fstr` project save/load with guarded autosave, recovery diagnostics, floor deletion warnings, and persisted member locks.
+- `.fstr` project save/load with guarded autosave, immutable pre-overwrite revisions, destructive-change review, recovery diagnostics, floor deletion warnings, and persisted member locks.
- Stair Builder geometry with destination slab openings, DXF footprint output, and 3D review.
- Reports and schedules for columns, beams, slabs, footings/base reactions, and preliminary design summaries.
- Coordinated multi-floor DXF drawing packages, IFC2x3, STAAD.Pro, ETABS 22 OAPI, and ETABS-to-SAFE handoff paths.
@@ -99,7 +99,7 @@ python -m pip install -r v3/tools/requirements-dxf.txt
node v3/tools/check-fs.js
```
-The browser smoke covers initialization, plan geometry, slab ownership, cantilever behavior, persistence and recovery guards, member locking, measurement tools, stair persistence, 3D rendering, coordinated DXF completeness, strict DXF open/audit/save/reopen validation, and export payload parity.
+The browser smoke covers initialization, plan geometry, slab ownership, cantilever behavior, persistence and recovery guards, protected-revision restore/download, the 50-revision retention limit, historical `.fstr` migration, member locking, measurement tools, stair persistence, 3D rendering, coordinated DXF completeness, strict DXF open/audit/save/reopen validation, and export payload parity.
Where AutoCAD 2025 is installed, run the native read gate against a generated package without modifying it:
@@ -116,6 +116,7 @@ futolstructure/
|-- v3/
| |-- assets/ Product identity and screenshots
| |-- engine/ Extracted structural calculation helpers
+| |-- persistence/ Protected project revision storage
| |-- tools/ Regression smoke runner
| `-- index.html Current application
|-- index.html Hosted root entry
diff --git a/docs/FS-118_RELEASE_NOTES.md b/docs/FS-118_RELEASE_NOTES.md
new file mode 100644
index 0000000..e23cd53
--- /dev/null
+++ b/docs/FS-118_RELEASE_NOTES.md
@@ -0,0 +1,45 @@
+# FS-118 Protected Project Revisions
+
+- **Release:** FutolStructure v3.16.118
+- **Build:** FS-118
+- **Date:** 2026-07-14
+
+## Purpose
+
+FS-118 protects user-authored structural intent before an existing `.fstr` file is overwritten. It builds on the production FS-117 R12 DXF baseline without changing the DXF envelope, structural analysis formulas, solver exporters, or active-model geometry rules.
+
+## Included
+
+- Immutable IndexedDB revision records containing a cloned project payload, source identity, schema/build provenance, structural summary, health assessment, and model delta.
+- Automatic pre-overwrite capture for browser file-handle saves.
+- A guarded save review when floors, members, locks, offsets, cantilevers, or corner slabs are removed, or when void/deletion counts increase.
+- A health gate that quarantines implausible autosaves and can restore the last known healthy browser snapshot.
+- Recovery UI for revision history, restore, and `.fstr` download.
+- Per-project retention of the newest 50 protected revisions.
+- Project and source-revision identity embedded in new `.fstr` saves and report provenance.
+- A legacy v2.8 two-floor fixture proving hidden geometry, member locks, and beam offsets survive migration and repeated round trips.
+
+## Storage Boundary
+
+Protected revisions are local to the browser profile and device through IndexedDB. They are not cloud backups and do not replace dated external `.fstr` files. Clearing site data or using another browser/profile removes access to that local revision history.
+
+## Acceptance Gates
+
+- Inline and external JavaScript syntax checks.
+- Full Chrome/Edge browser regression.
+- Pre-overwrite capture, destructive-delta detection, invalid-health blocking, restore, and revision download.
+- Exact retention of revisions 5 through 54 after 55 ordered writes, proving a 50-record cap.
+- Legacy v2.8 `.fstr` load, migration, and two successive round trips.
+- Current Olango three-floor project smoke.
+- Unchanged FS-117 strict AutoCAD R12 parser round trip and native AutoCAD read gate.
+
+## Excluded
+
+- Cloud synchronization, user accounts, shared project history, and server-side backup.
+- Historical helper scripts and unrelated workspace edits.
+- Structural solver, stair, IFC, STAAD, ETABS, SAFE, or broad UI changes.
+- Changes to the FS-117 DXF R12 writer.
+
+## Rollback
+
+If FS-118 fails in production, revert only the FS-118 merge commit. FS-117 remains the known-good DXF production baseline.
diff --git a/docs/RELEASE_HYGIENE.md b/docs/RELEASE_HYGIENE.md
index ae00444..ad9f254 100644
--- a/docs/RELEASE_HYGIENE.md
+++ b/docs/RELEASE_HYGIENE.md
@@ -20,6 +20,7 @@ Use this checklist before pushing a public FutolStructure update.
- `v3/assets/futolstructure-icon.png`
- `v3/assets/screenshots/`
- `v3/engine/`
+- `v3/persistence/`
- `v3/dxf-export.js`
- `v3/tools/check-fs.js`
- `v3/tools/check-dxf-autocad.ps1`
@@ -43,6 +44,8 @@ python -m pip install -r v3/tools/requirements-dxf.txt
node v3/tools/check-fs.js
```
+For persistence releases, the browser gate must also prove pre-overwrite capture, destructive-delta review, health blocking, restore/download behavior, exact 50-revision retention, and legacy `.fstr` round-trip compatibility.
+
For a DXF release on a workstation with AutoCAD 2025:
```powershell
diff --git a/v3/dxf-export.js b/v3/dxf-export.js
index 502449d..aba1f7c 100644
--- a/v3/dxf-export.js
+++ b/v3/dxf-export.js
@@ -724,7 +724,7 @@
stackY = indexTable.bottomY - 1;
const modelRows = [
- { item: 'Build', value: DXF_PACKAGE_BUILD },
+ { item: 'Build', value: `${audit.build} / DXF ${audit.writerBuild}` },
{ item: 'FSTR schema', value: window.getProjectProvenance?.().fstrSchemaVersion || 'unknown' },
{ item: 'Source revision', value: window.getProjectProvenance?.().sourceRevisionId || 'unsaved working state' },
{ item: 'Floors', value: state.floors?.length || 0 },
@@ -848,8 +848,10 @@
const floorGeometryById = collect3DFloorGeometry();
const planBounds = calculatePlanBounds(floorGeometryById);
const writer = new DxfWriter();
+ const releaseBuild = window.getReleaseManifest?.().buildId || DXF_PACKAGE_BUILD;
const audit = {
- build: DXF_PACKAGE_BUILD,
+ build: releaseBuild,
+ writerBuild: DXF_PACKAGE_BUILD,
layerSource: 'FT_LayerMap.xlsx structural mapping embedded in FutolStructure',
floorIds: state.floors.map(floor => floor.id),
plans: { layouts: 0, tributary: 0, foundation: 0 },
@@ -861,7 +863,7 @@
const cellHeight = planBounds.height + 5.2;
const packageTop = 0;
writer.text(0, packageTop + 4.1, 'FUTOLSTRUCTURE - COORDINATED DXF DRAWING PACKAGE', DXF_LAYER.TEXT, 0.48);
- writer.text(0, packageTop + 3.45, `${DXF_PACKAGE_BUILD} | UNITS: METERS | GENERATED ${localDateStamp()}`, DXF_LAYER.TEXT, 0.2);
+ writer.text(0, packageTop + 3.45, `${releaseBuild} | DXF WRITER ${DXF_PACKAGE_BUILD} | UNITS: METERS | GENERATED ${localDateStamp()}`, DXF_LAYER.TEXT, 0.2);
writer.text(0, packageTop + 3.05, 'Plans, schedules, load summary, and preliminary BOQ use FT structural layer mapping.', DXF_LAYER.TEXT, 0.18);
(state.floors || []).forEach((floor, rowIndex) => {
diff --git a/v3/index.html b/v3/index.html
index 8e16f5a..b195f8e 100644
--- a/v3/index.html
+++ b/v3/index.html
@@ -1821,7 +1821,7 @@
FutolStructure
-
+