Migration.mo
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