verify-canister-migration.mjs
165 lines 5.3 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 #!/usr/bin/env node
2 /**
3 * Static checks that hub/icp stable-memory migration contracts are still present.
4 * Fails fast if Migration.mo or main.mo drift in ways that risk an incompatible upgrade.
5 *
6 * Run: node scripts/verify-canister-migration.mjs
7 * (Also invoked from scripts/canister-predeploy.sh)
8 */
9 import fs from 'fs';
10 import path from 'path';
11 import { fileURLToPath } from 'url';
12
13 const __dirname = path.dirname(fileURLToPath(import.meta.url));
14 const REPO_ROOT = path.join(__dirname, '..');
15 const MIGRATION = path.join(REPO_ROOT, 'hub/icp/src/hub/Migration.mo');
16 const MAIN = path.join(REPO_ROOT, 'hub/icp/src/hub/main.mo');
17 const JSON_VALIDATE = path.join(REPO_ROOT, 'hub/icp/src/hub/JsonValidate.mo');
18
19 function readUtf8(p) {
20 return fs.readFileSync(p, 'utf8');
21 }
22
23 const migrationChecks = [
24 {
25 name: 'StableStorageV0: vaultEntries is (userId, pathMap) — pre–Phase-15.1',
26 ok: (s) => s.includes('vaultEntries : [(Text, [(Text, (Text, Text))])];'),
27 },
28 {
29 name: 'StableStorage V1: vaultEntries is (userId, vaultId, pathMap)',
30 ok: (s) => s.includes('vaultEntries : [(Text, Text, [(Text, (Text, Text))])];'),
31 },
32 {
33 name: 'StableStorage V1: billingByUser reserved (hosted billing roadmap)',
34 ok: (s) => s.includes('billingByUser : [(Text, BillingRecord)];'),
35 },
36 {
37 name: 'ProposalRecordV1 includes vault_id field (pre–evaluation)',
38 ok: (s) => s.includes('external_ref : Text;\n vault_id : Text;\n created_at : Text;'),
39 },
40 {
41 name: 'ProposalRecord (V2) includes evaluation_status',
42 ok: (s) => s.includes('evaluation_status : Text;') && s.includes('evaluation_waiver_json : Text;'),
43 },
44 {
45 name: 'migrateFromV0ToV1(old : { var storage : StableStorageV0 }) — historical V0→V1',
46 ok: (s) => s.includes('migrateFromV0ToV1(old : { var storage : StableStorageV0 })'),
47 },
48 {
49 name: 'migration(old : { var storage : StableStorageV7 }) — V7→V8 adds cors_allowed_origin',
50 ok: (s) => s.includes('migration(old : { var storage : StableStorageV7 })'),
51 },
52 {
53 name: 'StableStorageV5 — pre-V6 on-chain layout',
54 ok: (s) => s.includes('public type StableStorageV5'),
55 },
56 {
57 name: 'StableStorageV6 — pre-V7 on-chain layout (has operator_export_secret)',
58 ok: (s) => s.includes('public type StableStorageV6'),
59 },
60 {
61 name: 'StableStorageV7 — pre-V8 on-chain layout (has gateway_auth_secret)',
62 ok: (s) => s.includes('public type StableStorageV7'),
63 },
64 {
65 name: 'StableStorage (V8) includes operator_export_secret, gateway_auth_secret, cors_allowed_origin',
66 ok: (s) =>
67 s.includes('operator_export_secret : Text') &&
68 s.includes('gateway_auth_secret : Text') &&
69 s.includes('cors_allowed_origin : Text'),
70 },
71 {
72 name: 'StableStorageV4 type (pre-V5 proposals)',
73 ok: (s) => s.includes('public type StableStorageV4') && s.includes('[ProposalRecordV4]'),
74 },
75 {
76 name: 'ProposalRecord includes enrich + suggested frontmatter JSON (V5)',
77 ok: (s) =>
78 s.includes('assistant_notes : Text;') &&
79 s.includes('suggested_labels_json : Text;') &&
80 s.includes('assistant_suggested_frontmatter_json : Text;'),
81 },
82 {
83 name: 'V0 → V1 maps notes into vault "default"',
84 ok: (s) => s.includes('(entry.0, "default", entry.1)'),
85 },
86 {
87 name: 'V0 proposals gain vault_id "default"',
88 ok: (s) => s.includes('vault_id = "default"') && s.includes('v0ToProposalV1'),
89 },
90 ];
91
92 const mainChecks = [
93 {
94 name: 'Actor uses Migration.migration hook',
95 ok: (s) => s.includes('(with migration = Migration.migration)'),
96 },
97 {
98 name: 'persistent actor Hub',
99 ok: (s) => s.includes('persistent actor Hub'),
100 },
101 {
102 name: 'Imports Migration module',
103 ok: (s) => s.includes('import Migration "Migration"'),
104 },
105 {
106 name: 'Imports JsonValidate + normalizes enrich fragments on GET proposal',
107 ok: (s) =>
108 s.includes('import JsonValidate "JsonValidate"') &&
109 s.includes('JsonValidate.normalizeJsonArrayFragment') &&
110 s.includes('JsonValidate.prepareEnrichJsonArray'),
111 },
112 {
113 name: 'Stable storage type matches Migration.StableStorage',
114 ok: (s) => s.includes('type StableStorage = Migration.StableStorage'),
115 },
116 ];
117
118 let failed = 0;
119
120 for (const { name, ok } of migrationChecks) {
121 const text = readUtf8(MIGRATION);
122 if (!ok(text)) {
123 console.error(`FAIL: ${name}\n file: ${MIGRATION}`);
124 failed++;
125 }
126 }
127
128 for (const { name, ok } of mainChecks) {
129 const text = readUtf8(MAIN);
130 if (!ok(text)) {
131 console.error(`FAIL: ${name}\n file: ${MAIN}`);
132 failed++;
133 }
134 }
135
136 const jsonValidateChecks = [
137 {
138 name: 'JsonValidate.mo: enrich prepare + GET normalize helpers',
139 ok: (s) =>
140 s.includes('prepareEnrichJsonArray') &&
141 s.includes('prepareEnrichJsonObject') &&
142 s.includes('normalizeJsonArrayFragment') &&
143 s.includes('normalizeJsonObjectFragment'),
144 },
145 ];
146
147 if (!fs.existsSync(JSON_VALIDATE)) {
148 console.error(`FAIL: JsonValidate.mo missing\n file: ${JSON_VALIDATE}`);
149 failed++;
150 } else {
151 const jv = readUtf8(JSON_VALIDATE);
152 for (const { name, ok } of jsonValidateChecks) {
153 if (!ok(jv)) {
154 console.error(`FAIL: ${name}\n file: ${JSON_VALIDATE}`);
155 failed++;
156 }
157 }
158 }
159
160 if (failed > 0) {
161 console.error(`\nverify-canister-migration: ${failed} check(s) failed.`);
162 process.exit(1);
163 }
164
165 console.log('verify-canister-migration: OK (Migration.mo + main.mo contracts).');
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago