1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! The PARTS-LIBRARY document lane — the one place the app rewrites a
//! parts-library entry from a part document, and the one place it writes a part
//! document back to the file it came from.
//!
//! Two callers ride it: the update-components batch
//! ([`crate::panels::update_components`]), which pulls a changed source file
//! into every instance, and the BOM's part-attribute edits
//! ([`crate::panels::bom`]), which change a part document in place and must
//! reach that part's file. A second copy of either rule is exactly the drift
//! this module exists to prevent.
//!
//! # The refresh lane — document transport, not a UI-side kernel call
//!
//! The kernel's `refresh_library_entry` mutates the RUNNER-side thread-local
//! library (the thread/worker that runs `execute_history`); calling it from the
//! UI thread under the Thread/Worker runner would mutate a cold store nobody
//! executes against (it only *looks* right under the test-time Inline runner).
//! A refresh therefore rides the DOCUMENT: rewrite the assembly's
//! `partsLibrary[part]` entry — new `document`, a fresh content-hash
//! `sourceSignature`, and a BLANKED `snapshot` — and reload.
//! `set_history_json` resets the runner (clearing its resident library), the
//! next run ingests the block, and the empty snapshot forces the kernel's
//! designed SELF-HEAL lane: re-execute the embedded document, re-snapshot,
//! rebuild every instance, re-solve. Correct under every runner.
//!
//! # WRITE-THROUGH — saving an edited part back to its SOURCE file
//!
//! Changing a part document in the assembly is an IN-MEMORY update; on its own
//! it persists only if the user then saves the ASSEMBLY, leaving the part's
//! `.BREP.json` silently behind (and — because [`crate::panels::update_components`]
//! compares the store's content against the entry's `sourceSignature` — badging
//! the component "outdated" against a file it is actually NEWER than).
//! [`write_through`] writes the edited part document back to the `sourceKey` it
//! came from, so the entry's signature and the file agree and the badge stays
//! dark. Five lanes:
//!
//! * **embedded-only** (`sourceKey: ""`): no file exists to write back to.
//! Skipped SILENTLY. This used to be EVERY STEP-imported part (build-spec
//! §3.5); a STEP import now writes its unique parts to the store and stamps a
//! real key, so the empty key is what a part gets when there was no store to
//! write to — a headless import, or a write that failed.
//! * **source gone** (the file was deleted or renamed since insertion): NOT
//! recreated — resurrecting a file the user removed is a worse surprise than
//! a note. The assembly keeps the edit; a notice says the file was not
//! updated.
//! * **source moved on** (the file was edited + saved elsewhere since the
//! assembly last synced with it — the very case the outdated badge catches):
//! NOT overwritten. Clobbering it would destroy work that lives nowhere
//! else; the assembly keeps the edit, the notice names the file, and the
//! badge (now genuinely divergent) stays lit.
//! * **write failed**: the edit still lands in the assembly — losing it is the
//! worst outcome, a stale file is recoverable — with a notice carrying the
//! backend's error. (A WRITE-BEHIND backend can also fail LATER; that lands
//! in `ModelStore::take_persistence_errors`, which the shell already drains
//! into the toast overlay every frame.)
//! * **written**: a notice naming the part AND the file, so the side effect on
//! a document shared by other assemblies is visible rather than hidden.
use crateModelStore;
use EngineState;
/// Rewrite `partsLibrary[part_name]` in a parsed assembly document with a NEW
/// part document text: the embedded `document`, a fresh [`document_signature`],
/// and a BLANKED `snapshot`. The blank snapshot is the heal trigger: the
/// entry's resident `dirty` flag cannot ride the document (serde-skipped), but
/// an unreadable snapshot routes the ACOMP feature into the SAME self-heal lane
/// (re-execute `document`, re-snapshot, rebuild every instance, re-solve).
///
/// The ONE "refresh a library entry through the document" step (the
/// document-transport lane in this module's header). The caller reloads the
/// rewritten document (`set_history_json`) once — batch callers rewrite every
/// entry first, then reload.
/// WRITE-THROUGH: save `edited` (the part document the caller just changed) back
/// to the store document it came from, so the file and the assembly's embedded
/// copy stay one thing — and the update-components badge, which compares
/// [`document_signature`] of the file against the entry's `sourceSignature`,
/// reads a freshly edited part as up-to-date instead of backwards.
///
/// `target` is the entry's `(sourceKey, sourceSignature)` as it stood BEFORE the
/// edit re-stamped the signature: the key names the file, the signature is the
/// content the assembly last SYNCED with that file. `None` for an EMBEDDED-ONLY
/// entry (an empty `sourceKey`) — there is no file to write back to.
///
/// The module header lists the lanes. EVERY refusal leaves the caller's
/// in-memory change standing and only pushes a notice: a stale file is
/// recoverable, a dropped edit is not.
pub
/// The ONE signature fn, re-exported from its engine-altitude home
/// ([`brep_render::engine_state::document_signature`]) so the app's writers —
/// the insert flow (`panels::file`), [`refresh_library_entry`], the
/// update-components comparison (`panels::update_components`) — and any
/// engine-side writer hash a part document identically. Kept on this path
/// because this module is the app's documented signature/refresh home.
pub use document_signature;
// BREP private tests: 7890b3ac6c0982c2