Migration.mo
458 lines 15.2 KB
Raw
sha256:7fe782411f3a68b39f17777b274349b30752365c13438b26bfd602c687de2c65 feat(web): rebrand Parentier family to Ourware on landing Human minor ⚠ breaking 18 days ago
1 /**
2 * Stable-memory upgrade: V0 = one note map per user (vaultEntries 2-tuple).
3 * V1 = notes keyed by (userId, vaultId, path); proposals carry vault_id; billing fields reserved per HOSTED-STORAGE-BILLING-ROADMAP.
4 * V2 = ProposalRecord gains human evaluation fields (Text; checklist JSON in evaluation_checklist).
5 * V3 = ProposalRecord gains review_queue, review_severity, auto_flag_reasons_json, review_hints* (Text).
6 * V4 = ProposalRecord gains assistant_notes, assistant_model, assistant_at, suggested_labels_json (LLM Enrich).
7 * V5 = ProposalRecord gains assistant_suggested_frontmatter_json (structured Enrich metadata).
8 * Actor upgrade hook: after mainnet has run **one** V7→`created_by` deploy (SEC-KN-4 T1), stable storage is
9 * `StableStorage` (proposals carry `created_by`). The hook is **identity** on `StableStorage` so repeat
10 * deploys succeed (SEC-KN-4c / T4). Canisters still on pre-`created_by` `StableStorageV7` must install
11 * **once** a WASM whose `migration` input is `StableStorageV7` (see git history before this identity
12 * restore), then upgrade to this tree.
13 * See https://internetcomputer.org/docs/motoko/fundamentals/actors/compatibility
14 */
15 import Array "mo:base/Array";
16
17 module Migration {
18 /// Shape persisted before Phase 15.1 (hosted multi-vault + billing reservation).
19 public type ProposalRecordV0 = {
20 proposal_id : Text;
21 path : Text;
22 status : Text;
23 body : Text;
24 frontmatter : Text;
25 intent : Text;
26 base_state_id : Text;
27 external_ref : Text;
28 created_at : Text;
29 updated_at : Text;
30 };
31
32 public type StableStorageV0 = {
33 vaultEntries : [(Text, [(Text, (Text, Text))])];
34 proposalEntries : [(Text, [ProposalRecordV0])];
35 };
36
37 /// Reserved for Phase 16; mirrored from gateway when needed. Defaults on migration: beta tier, zero balances.
38 public type BillingRecord = {
39 tier : Text;
40 stripe_customer_id : Text;
41 stripe_subscription_id : Text;
42 period_start : Text;
43 period_end : Text;
44 monthly_included_cents : Nat;
45 monthly_used_cents : Nat;
46 addon_cents : Nat;
47 };
48
49 /// Proposals as persisted before evaluation fields (mainnet V1).
50 public type ProposalRecordV1 = {
51 proposal_id : Text;
52 path : Text;
53 status : Text;
54 body : Text;
55 frontmatter : Text;
56 intent : Text;
57 base_state_id : Text;
58 external_ref : Text;
59 vault_id : Text;
60 created_at : Text;
61 updated_at : Text;
62 };
63
64 public type StableStorageV1 = {
65 vaultEntries : [(Text, Text, [(Text, (Text, Text))])];
66 proposalEntries : [(Text, [ProposalRecordV1])];
67 billingByUser : [(Text, BillingRecord)];
68 };
69
70 /// Proposals as persisted before review-queue / auto-flag / LLM hints (pre-V3 upgrade).
71 public type ProposalRecordV2 = {
72 proposal_id : Text;
73 path : Text;
74 status : Text;
75 body : Text;
76 frontmatter : Text;
77 intent : Text;
78 base_state_id : Text;
79 external_ref : Text;
80 vault_id : Text;
81 created_at : Text;
82 updated_at : Text;
83 evaluation_status : Text;
84 evaluation_grade : Text;
85 evaluation_checklist : Text;
86 evaluation_comment : Text;
87 evaluated_by : Text;
88 evaluated_at : Text;
89 evaluation_waiver_json : Text;
90 };
91
92 public type StableStorageV2 = {
93 vaultEntries : [(Text, Text, [(Text, (Text, Text))])];
94 proposalEntries : [(Text, [ProposalRecordV2])];
95 billingByUser : [(Text, BillingRecord)];
96 };
97
98 /// Proposals as persisted before LLM Enrich fields (pre-V4 upgrade).
99 public type ProposalRecordBeforeEnrich = {
100 proposal_id : Text;
101 path : Text;
102 status : Text;
103 body : Text;
104 frontmatter : Text;
105 intent : Text;
106 base_state_id : Text;
107 external_ref : Text;
108 vault_id : Text;
109 created_at : Text;
110 updated_at : Text;
111 evaluation_status : Text;
112 evaluation_grade : Text;
113 evaluation_checklist : Text;
114 evaluation_comment : Text;
115 evaluated_by : Text;
116 evaluated_at : Text;
117 evaluation_waiver_json : Text;
118 review_queue : Text;
119 review_severity : Text;
120 auto_flag_reasons_json : Text;
121 review_hints : Text;
122 review_hints_at : Text;
123 review_hints_model : Text;
124 };
125
126 /// Proposals as persisted after V4 enrich, before V5 suggested frontmatter JSON.
127 public type ProposalRecordV4 = {
128 proposal_id : Text;
129 path : Text;
130 status : Text;
131 body : Text;
132 frontmatter : Text;
133 intent : Text;
134 base_state_id : Text;
135 external_ref : Text;
136 vault_id : Text;
137 created_at : Text;
138 updated_at : Text;
139 evaluation_status : Text;
140 evaluation_grade : Text;
141 evaluation_checklist : Text;
142 evaluation_comment : Text;
143 evaluated_by : Text;
144 evaluated_at : Text;
145 evaluation_waiver_json : Text;
146 review_queue : Text;
147 review_severity : Text;
148 auto_flag_reasons_json : Text;
149 review_hints : Text;
150 review_hints_at : Text;
151 review_hints_model : Text;
152 assistant_notes : Text;
153 assistant_model : Text;
154 assistant_at : Text;
155 suggested_labels_json : Text;
156 };
157
158 /// Proposals as persisted before SEC-KN-4 `created_by` (on-chain V7 layout).
159 public type ProposalRecordV7 = {
160 proposal_id : Text;
161 path : Text;
162 status : Text;
163 body : Text;
164 frontmatter : Text;
165 intent : Text;
166 base_state_id : Text;
167 external_ref : Text;
168 vault_id : Text;
169 created_at : Text;
170 updated_at : Text;
171 evaluation_status : Text;
172 evaluation_grade : Text;
173 evaluation_checklist : Text;
174 evaluation_comment : Text;
175 evaluated_by : Text;
176 evaluated_at : Text;
177 evaluation_waiver_json : Text;
178 review_queue : Text;
179 review_severity : Text;
180 auto_flag_reasons_json : Text;
181 review_hints : Text;
182 review_hints_at : Text;
183 review_hints_model : Text;
184 assistant_notes : Text;
185 assistant_model : Text;
186 assistant_at : Text;
187 suggested_labels_json : Text;
188 assistant_suggested_frontmatter_json : Text;
189 };
190
191 public type ProposalRecord = {
192 proposal_id : Text;
193 path : Text;
194 status : Text;
195 body : Text;
196 frontmatter : Text;
197 intent : Text;
198 base_state_id : Text;
199 external_ref : Text;
200 vault_id : Text;
201 created_at : Text;
202 updated_at : Text;
203 evaluation_status : Text;
204 evaluation_grade : Text;
205 evaluation_checklist : Text;
206 evaluation_comment : Text;
207 evaluated_by : Text;
208 evaluated_at : Text;
209 evaluation_waiver_json : Text;
210 review_queue : Text;
211 review_severity : Text;
212 auto_flag_reasons_json : Text;
213 review_hints : Text;
214 review_hints_at : Text;
215 review_hints_model : Text;
216 assistant_notes : Text;
217 assistant_model : Text;
218 assistant_at : Text;
219 suggested_labels_json : Text;
220 assistant_suggested_frontmatter_json : Text;
221 created_by : Text;
222 };
223
224 public type StableStorageBeforeEnrich = {
225 vaultEntries : [(Text, Text, [(Text, (Text, Text))])];
226 proposalEntries : [(Text, [ProposalRecordBeforeEnrich])];
227 billingByUser : [(Text, BillingRecord)];
228 };
229
230 public type StableStorageV4 = {
231 vaultEntries : [(Text, Text, [(Text, (Text, Text))])];
232 proposalEntries : [(Text, [ProposalRecordV4])];
233 billingByUser : [(Text, BillingRecord)];
234 };
235
236 /// Stable layout on mainnet **before** `operator_export_secret` (V5 on-chain).
237 public type StableStorageV5 = {
238 vaultEntries : [(Text, Text, [(Text, (Text, Text))])];
239 proposalEntries : [(Text, [ProposalRecordV7])];
240 billingByUser : [(Text, BillingRecord)];
241 };
242
243 /// Stable layout on mainnet **before** `gateway_auth_secret` (V6 on-chain).
244 public type StableStorageV6 = {
245 vaultEntries : [(Text, Text, [(Text, (Text, Text))])];
246 proposalEntries : [(Text, [ProposalRecordV7])];
247 billingByUser : [(Text, BillingRecord)];
248 operator_export_secret : Text;
249 };
250
251 /// On-chain layout after `gateway_auth_secret` **and** `cors_allowed_origin` (hosted CORS lock).
252 /// Must match serialized mainnet state before upgrades that only change `StableStorage` / `ProposalRecord`.
253 public type StableStorageV7 = {
254 vaultEntries : [(Text, Text, [(Text, (Text, Text))])];
255 proposalEntries : [(Text, [ProposalRecordV7])];
256 billingByUser : [(Text, BillingRecord)];
257 operator_export_secret : Text;
258 gateway_auth_secret : Text;
259 cors_allowed_origin : Text;
260 };
261
262 public type StableStorage = {
263 vaultEntries : [(Text, Text, [(Text, (Text, Text))])];
264 proposalEntries : [(Text, [ProposalRecord])];
265 billingByUser : [(Text, BillingRecord)];
266 operator_export_secret : Text;
267 gateway_auth_secret : Text;
268 cors_allowed_origin : Text;
269 };
270
271 func _proposalBeforeEnrichToCurrent(p : ProposalRecordBeforeEnrich) : ProposalRecordV7 {
272 {
273 proposal_id = p.proposal_id;
274 path = p.path;
275 status = p.status;
276 body = p.body;
277 frontmatter = p.frontmatter;
278 intent = p.intent;
279 base_state_id = p.base_state_id;
280 external_ref = p.external_ref;
281 vault_id = p.vault_id;
282 created_at = p.created_at;
283 updated_at = p.updated_at;
284 evaluation_status = p.evaluation_status;
285 evaluation_grade = p.evaluation_grade;
286 evaluation_checklist = p.evaluation_checklist;
287 evaluation_comment = p.evaluation_comment;
288 evaluated_by = p.evaluated_by;
289 evaluated_at = p.evaluated_at;
290 evaluation_waiver_json = p.evaluation_waiver_json;
291 review_queue = p.review_queue;
292 review_severity = p.review_severity;
293 auto_flag_reasons_json = p.auto_flag_reasons_json;
294 review_hints = p.review_hints;
295 review_hints_at = p.review_hints_at;
296 review_hints_model = p.review_hints_model;
297 assistant_notes = "";
298 assistant_model = "";
299 assistant_at = "";
300 suggested_labels_json = "[]";
301 assistant_suggested_frontmatter_json = "{}";
302 };
303 };
304
305 /// Historical V4→V5 row map; kept for docs and one-off tooling. Actor hook uses identity on `StableStorage` once V5 is live.
306 func _proposalV4ToV5(p : ProposalRecordV4) : ProposalRecordV7 {
307 {
308 proposal_id = p.proposal_id;
309 path = p.path;
310 status = p.status;
311 body = p.body;
312 frontmatter = p.frontmatter;
313 intent = p.intent;
314 base_state_id = p.base_state_id;
315 external_ref = p.external_ref;
316 vault_id = p.vault_id;
317 created_at = p.created_at;
318 updated_at = p.updated_at;
319 evaluation_status = p.evaluation_status;
320 evaluation_grade = p.evaluation_grade;
321 evaluation_checklist = p.evaluation_checklist;
322 evaluation_comment = p.evaluation_comment;
323 evaluated_by = p.evaluated_by;
324 evaluated_at = p.evaluated_at;
325 evaluation_waiver_json = p.evaluation_waiver_json;
326 review_queue = p.review_queue;
327 review_severity = p.review_severity;
328 auto_flag_reasons_json = p.auto_flag_reasons_json;
329 review_hints = p.review_hints;
330 review_hints_at = p.review_hints_at;
331 review_hints_model = p.review_hints_model;
332 assistant_notes = p.assistant_notes;
333 assistant_model = p.assistant_model;
334 assistant_at = p.assistant_at;
335 suggested_labels_json = p.suggested_labels_json;
336 assistant_suggested_frontmatter_json = "{}";
337 };
338 };
339
340 func v0ToProposalV1(p : ProposalRecordV0) : ProposalRecordV1 {
341 {
342 proposal_id = p.proposal_id;
343 path = p.path;
344 status = p.status;
345 body = p.body;
346 frontmatter = p.frontmatter;
347 intent = p.intent;
348 base_state_id = p.base_state_id;
349 external_ref = p.external_ref;
350 vault_id = "default";
351 created_at = p.created_at;
352 updated_at = p.updated_at;
353 };
354 };
355
356 func v1ToV2Eval(p : ProposalRecordV1) : ProposalRecordV2 {
357 {
358 proposal_id = p.proposal_id;
359 path = p.path;
360 status = p.status;
361 body = p.body;
362 frontmatter = p.frontmatter;
363 intent = p.intent;
364 base_state_id = p.base_state_id;
365 external_ref = p.external_ref;
366 vault_id = p.vault_id;
367 created_at = p.created_at;
368 updated_at = p.updated_at;
369 evaluation_status = "";
370 evaluation_grade = "";
371 evaluation_checklist = "";
372 evaluation_comment = "";
373 evaluated_by = "";
374 evaluated_at = "";
375 evaluation_waiver_json = "";
376 };
377 };
378
379 /// One-time V0→V1 transform (Phase 15.1). **Not** the actor upgrade hook: mainnet stable is already V1.
380 /// If you still have a V0 canister, deploy an older release that used `migration` from V0 first, or reinstall.
381 public func migrateFromV0ToV1(old : { var storage : StableStorageV0 }) : { var storage : StableStorageV1 } {
382 {
383 var storage = {
384 vaultEntries = Array.map<(Text, [(Text, (Text, Text))]), (Text, Text, [(Text, (Text, Text))])>(
385 old.storage.vaultEntries,
386 func(entry : (Text, [(Text, (Text, Text))])) : (Text, Text, [(Text, (Text, Text))]) {
387 (entry.0, "default", entry.1);
388 },
389 );
390 proposalEntries = Array.map<(Text, [ProposalRecordV0]), (Text, [ProposalRecordV1])>(
391 old.storage.proposalEntries,
392 func(e : (Text, [ProposalRecordV0])) : (Text, [ProposalRecordV1]) {
393 (e.0, Array.map<ProposalRecordV0, ProposalRecordV1>(e.1, v0ToProposalV1));
394 },
395 );
396 billingByUser = [];
397 };
398 };
399 };
400
401 /// V1 → V2eval (offline / historical): add evaluation Text fields. Not used as the actor hook once mainnet is V2.
402 public func migrateFromV1ToV2Eval(old : { var storage : StableStorageV1 }) : { var storage : StableStorageV2 } {
403 {
404 var storage = {
405 vaultEntries = old.storage.vaultEntries;
406 billingByUser = old.storage.billingByUser;
407 proposalEntries = Array.map<(Text, [ProposalRecordV1]), (Text, [ProposalRecordV2])>(
408 old.storage.proposalEntries,
409 func(e : (Text, [ProposalRecordV1])) : (Text, [ProposalRecordV2]) {
410 (e.0, Array.map<ProposalRecordV1, ProposalRecordV2>(e.1, v1ToV2Eval));
411 },
412 );
413 };
414 };
415 };
416
417 func _proposalV7ToCurrent(p : ProposalRecordV7) : ProposalRecord {
418 {
419 proposal_id = p.proposal_id;
420 path = p.path;
421 status = p.status;
422 body = p.body;
423 frontmatter = p.frontmatter;
424 intent = p.intent;
425 base_state_id = p.base_state_id;
426 external_ref = p.external_ref;
427 vault_id = p.vault_id;
428 created_at = p.created_at;
429 updated_at = p.updated_at;
430 evaluation_status = p.evaluation_status;
431 evaluation_grade = p.evaluation_grade;
432 evaluation_checklist = p.evaluation_checklist;
433 evaluation_comment = p.evaluation_comment;
434 evaluated_by = p.evaluated_by;
435 evaluated_at = p.evaluated_at;
436 evaluation_waiver_json = p.evaluation_waiver_json;
437 review_queue = p.review_queue;
438 review_severity = p.review_severity;
439 auto_flag_reasons_json = p.auto_flag_reasons_json;
440 review_hints = p.review_hints;
441 review_hints_at = p.review_hints_at;
442 review_hints_model = p.review_hints_model;
443 assistant_notes = p.assistant_notes;
444 assistant_model = p.assistant_model;
445 assistant_at = p.assistant_at;
446 suggested_labels_json = p.suggested_labels_json;
447 assistant_suggested_frontmatter_json = p.assistant_suggested_frontmatter_json;
448 created_by = "";
449 };
450 };
451
452 /// Actor upgrade hook: input type must match **current** on-chain `storage` before this WASM installs.
453 /// Identity on `StableStorage` after the one-time V7→`created_by` upgrade (SEC-KN-4 T1) has run.
454 /// SEC-KN-4c / T4 — restores deployability; repeat upgrades no longer hit M0216.
455 public func migration(old : { var storage : StableStorage }) : { var storage : StableStorage } {
456 old
457 };
458 }
File History 1 commit
sha256:7fe782411f3a68b39f17777b274349b30752365c13438b26bfd602c687de2c65 feat(web): rebrand Parentier family to Ourware on landing Human minor 18 days ago