@mindflight/mindbrain-personal-studio 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +2 -2
  2. package/build/client/_app/immutable/chunks/D0UIlUGZ.js +1 -0
  3. package/build/client/_app/immutable/chunks/D0UIlUGZ.js.br +0 -0
  4. package/build/client/_app/immutable/chunks/D0UIlUGZ.js.gz +0 -0
  5. package/build/client/_app/immutable/entry/{app.CVz6aYsT.js → app.CR-imLox.js} +2 -2
  6. package/build/client/_app/immutable/entry/app.CR-imLox.js.br +0 -0
  7. package/build/client/_app/immutable/entry/app.CR-imLox.js.gz +0 -0
  8. package/build/client/_app/immutable/entry/start.DV-AjeAB.js +1 -0
  9. package/build/client/_app/immutable/entry/start.DV-AjeAB.js.br +2 -0
  10. package/build/client/_app/immutable/entry/start.DV-AjeAB.js.gz +0 -0
  11. package/build/client/_app/immutable/nodes/{1.BBtxY46Q.js → 1.CTEedoSY.js} +1 -1
  12. package/build/client/_app/immutable/nodes/1.CTEedoSY.js.br +0 -0
  13. package/build/client/_app/immutable/nodes/1.CTEedoSY.js.gz +0 -0
  14. package/build/client/_app/version.json +1 -1
  15. package/build/client/_app/version.json.br +0 -0
  16. package/build/client/_app/version.json.gz +0 -0
  17. package/build/handler.js +4 -4
  18. package/build/index.js +4 -4
  19. package/build/server/chunks/chunks/{internal.js-D8EA_he2.js → internal.js-WOmQXGMa.js} +2 -2
  20. package/build/server/chunks/chunks/{internal.js-D8EA_he2.js.map → internal.js-WOmQXGMa.js.map} +1 -1
  21. package/build/server/chunks/entries/endpoints/api/graph/schema-registry/{_server.ts.js-Dyfsc-VA.js → _server.ts.js-CAsXxBRq.js} +7 -2
  22. package/build/server/chunks/entries/endpoints/api/graph/schema-registry/_server.ts.js-CAsXxBRq.js.map +1 -0
  23. package/build/server/chunks/{handler-BjXBCd1c.js → handler-BIDedSZq.js} +3 -3
  24. package/build/server/chunks/{handler-BjXBCd1c.js.map → handler-BIDedSZq.js.map} +1 -1
  25. package/build/server/chunks/{index.js-BJYcV8V7.js → index.js-YVPJa0so.js} +2 -2
  26. package/build/server/chunks/{index.js-BJYcV8V7.js.map → index.js-YVPJa0so.js.map} +1 -1
  27. package/build/server/chunks/{manifest.js-Ddaot0P4.js → manifest.js-aGRKuiqF.js} +4 -4
  28. package/build/server/chunks/{manifest.js-Ddaot0P4.js.map → manifest.js-aGRKuiqF.js.map} +1 -1
  29. package/build/server/chunks/nodes/{1.js-BRigw_9J.js → 1.js-Dhh3ErZY.js} +2 -2
  30. package/build/server/chunks/nodes/{1.js-BRigw_9J.js.map → 1.js-Dhh3ErZY.js.map} +1 -1
  31. package/package.json +6 -2
  32. package/build/client/_app/immutable/chunks/BmeSanva.js +0 -1
  33. package/build/client/_app/immutable/chunks/BmeSanva.js.br +0 -0
  34. package/build/client/_app/immutable/chunks/BmeSanva.js.gz +0 -0
  35. package/build/client/_app/immutable/entry/app.CVz6aYsT.js.br +0 -0
  36. package/build/client/_app/immutable/entry/app.CVz6aYsT.js.gz +0 -0
  37. package/build/client/_app/immutable/entry/start.Bt5tVOz8.js +0 -1
  38. package/build/client/_app/immutable/entry/start.Bt5tVOz8.js.br +0 -2
  39. package/build/client/_app/immutable/entry/start.Bt5tVOz8.js.gz +0 -0
  40. package/build/client/_app/immutable/nodes/1.BBtxY46Q.js.br +0 -0
  41. package/build/client/_app/immutable/nodes/1.BBtxY46Q.js.gz +0 -0
  42. package/build/server/chunks/entries/endpoints/api/graph/schema-registry/_server.ts.js-Dyfsc-VA.js.map +0 -1
  43. package/scripts/build-serenity-v6-concept-review-pack.mjs +0 -493
  44. package/scripts/build-serenity-v6-review-pack.mjs +0 -479
  45. package/scripts/create-serenity-production-v6.mjs +0 -627
  46. package/scripts/export-serenity-v6-backup.mjs +0 -178
  47. package/scripts/import-serenity-v6-user-decisions.mjs +0 -543
  48. package/scripts/materialize-serenity-v6-snapshots.mjs +0 -675
@@ -1,479 +0,0 @@
1
- #!/usr/bin/env node
2
- import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import { spawnSync } from 'node:child_process';
5
-
6
- const TARGET_WORKSPACE = 'serenity-production-v6';
7
- const DEFAULT_DB = '/home/dlamotte/.ghostcrab/databases/ghostcrab-serenity-v4-demos.sqlite';
8
- const DEFAULT_IN_DIR = 'docs/demo/export-audit/2026-06-26/serenity-production-v6-migration';
9
- const DEFAULT_OUT_DIR = 'docs/demo/export-audit/2026-06-26/serenity-production-v6-review-pack';
10
-
11
- const dbPath = valueAfter('--db') ?? process.env.GHOSTCRAB_SQLITE_PATH ?? DEFAULT_DB;
12
- const inDir = valueAfter('--in-dir') ?? DEFAULT_IN_DIR;
13
- const outDir = valueAfter('--out-dir') ?? DEFAULT_OUT_DIR;
14
-
15
- function valueAfter(flag) {
16
- const index = process.argv.indexOf(flag);
17
- return index >= 0 ? process.argv[index + 1] : null;
18
- }
19
-
20
- function readJson(name) {
21
- return JSON.parse(readFileSync(join(inDir, name), 'utf8'));
22
- }
23
-
24
- function writeJson(name, value) {
25
- writeFileSync(join(outDir, name), `${JSON.stringify(value, null, 2)}\n`);
26
- }
27
-
28
- function quote(value) {
29
- return `'${String(value).replaceAll("'", "''")}'`;
30
- }
31
-
32
- function runSqlJson(sql) {
33
- const result = spawnSync('sqlite3', ['-readonly', '-json', dbPath, sql], {
34
- encoding: 'utf8',
35
- maxBuffer: 1024 * 1024 * 64
36
- });
37
- if (result.status !== 0) {
38
- throw new Error(result.stderr || result.stdout || 'sqlite3 failed');
39
- }
40
- return JSON.parse(result.stdout || '[]');
41
- }
42
-
43
- function uniq(values) {
44
- return [...new Set(values.filter((value) => value !== null && value !== undefined && value !== ''))].sort();
45
- }
46
-
47
- function splitSources(sourceString) {
48
- return uniq(String(sourceString || '').split(','));
49
- }
50
-
51
- function normalizeName(value) {
52
- return String(value || '')
53
- .toLowerCase()
54
- .normalize('NFD')
55
- .replace(/[\u0300-\u036f]/g, '')
56
- .replace(/d[eu]_/g, '')
57
- .replace(/[^a-z0-9]+/g, '_')
58
- .replace(/^_+|_+$/g, '');
59
- }
60
-
61
- function localSchemaId(schemaId) {
62
- const value = String(schemaId || '');
63
- if (value.startsWith(`${TARGET_WORKSPACE}:`)) return value.slice(TARGET_WORKSPACE.length + 1);
64
- if (value.startsWith('serenity-v4:')) return value.slice('serenity-v4:'.length);
65
- return value;
66
- }
67
-
68
- function domainOfSchema(schemaId) {
69
- const local = localSchemaId(schemaId);
70
- return local.split(':')[0] || 'unknown';
71
- }
72
-
73
- function familyFromScope(scope) {
74
- const parts = String(scope || '').split(':');
75
- return parts[1] || 'unknown';
76
- }
77
-
78
- function relationFamily(relationType) {
79
- const relation = String(relationType);
80
- if (['referencer', 'supported_by', 'supporter', 'maps_to', 'sourced_from'].includes(relation)) {
81
- return 'evidence_or_provenance';
82
- }
83
- if (['contenir', 'rattacher', 'est_composee_d', 'assigner_gestion'].includes(relation)) {
84
- return 'structural_or_ownership';
85
- }
86
- if (['est_calcule_a_partir_de', 'est_emise_par', 'resulte_de'].includes(relation)) {
87
- return 'financial_or_accounting';
88
- }
89
- if (['donne_lieu_a', 'est_actee_par', 'fonde_une', 'peut_preceder', 'porte_sur'].includes(relation)) {
90
- return 'procedure_flow';
91
- }
92
- if (['activer', 'composer', 'declencher', 'evalue', 'peut_generer', 'produit_des'].includes(relation)) {
93
- return 'workflow_effect';
94
- }
95
- return 'unclassified';
96
- }
97
-
98
- function evidenceLevel(supportedScenarios, sources) {
99
- if (supportedScenarios.some((scenario) => scenario.workflow)) return 'A_explicit_workflow';
100
- if (supportedScenarios.length > 0) return 'B_scenario_required_edge';
101
- if (sources.length > 0 && sources.every((source) => /fake|smoke|mini_seed/i.test(source))) return 'D_generated_only';
102
- return 'C_implied_or_unmapped';
103
- }
104
-
105
- function proposedRelationDecision(relationType, evidence) {
106
- if (['referencer', 'supported_by', 'supporter'].includes(relationType)) return 'split_or_rename_required';
107
- if (evidence === 'A_explicit_workflow') return 'candidate_promote_renamed';
108
- if (evidence === 'B_scenario_required_edge') return 'review_source_target_before_promote';
109
- return 'keep_rejected';
110
- }
111
-
112
- function schemaDecision(schemaId, match) {
113
- if (match?.exact) return 'map_to_existing';
114
- if (match?.best) return 'review_possible_existing_mapping';
115
- if (schemaId.startsWith('serenity-v4:')) return 'rename_and_add_if_sop_valid';
116
- return 'add_or_rename_after_sop_review';
117
- }
118
-
119
- function loadV6Schemas() {
120
- const rows = runSqlJson(
121
- `SELECT COALESCE(NULLIF(json_extract(facets_json, '$.schema_id'), ''),
122
- NULLIF(json_extract(content, '$.schema_id'), ''),
123
- id) AS schema_id
124
- FROM agent_facts
125
- WHERE workspace_id = ${quote(TARGET_WORKSPACE)}
126
- AND schema_id = 'mindbrain:schema'
127
- ORDER BY schema_id`
128
- );
129
- const schemas = rows.map((row) => row.schema_id);
130
- const normalized = new Map();
131
- for (const schema of schemas) {
132
- normalized.set(normalizeName(localSchemaId(schema)), schema);
133
- }
134
- return { schemas, normalized };
135
- }
136
-
137
- function findSchemaMatch(schemaId, v6Schemas) {
138
- const local = localSchemaId(schemaId);
139
- const exactTarget = `${TARGET_WORKSPACE}:${local}`;
140
- if (v6Schemas.schemas.includes(exactTarget)) return { exact: exactTarget, best: exactTarget };
141
- if (v6Schemas.schemas.includes(schemaId)) return { exact: schemaId, best: schemaId };
142
- const normalized = normalizeName(local);
143
- const best = v6Schemas.normalized.get(normalized);
144
- if (best) return { exact: null, best };
145
-
146
- const [, domain, name] = schemaId.match(/^(?:serenity-v4:)?([^:]+):(.+)$/) || [];
147
- if (domain && name) {
148
- const nameNorm = normalizeName(name);
149
- const candidates = v6Schemas.schemas
150
- .filter((schema) => localSchemaId(schema).startsWith(`${domain}:`))
151
- .map((schema) => ({ schema, score: similarity(nameNorm, normalizeName(localSchemaId(schema).split(':').slice(1).join(':'))) }))
152
- .filter((candidate) => candidate.score >= 0.72)
153
- .sort((a, b) => b.score - a.score);
154
- if (candidates[0]) return { exact: null, best: candidates[0].schema, score: candidates[0].score };
155
- }
156
- return { exact: null, best: null };
157
- }
158
-
159
- function similarity(a, b) {
160
- const aParts = new Set(String(a).split('_').filter(Boolean));
161
- const bParts = new Set(String(b).split('_').filter(Boolean));
162
- if (aParts.size === 0 || bParts.size === 0) return 0;
163
- const intersection = [...aParts].filter((part) => bParts.has(part)).length;
164
- const union = new Set([...aParts, ...bParts]).size;
165
- return intersection / union;
166
- }
167
-
168
- function buildScenarioMatrix(scenarios) {
169
- return scenarios.map((scenario) => ({
170
- artifact_id: scenario.artifact_id,
171
- scope: scenario.scope,
172
- public_label: scenario.public_label,
173
- projection_id: scenario.projection_id,
174
- sop_family: scenario.workflow ?? `legacy:${familyFromScope(scenario.scope)}`,
175
- evidence_level: scenario.workflow ? 'A_explicit_workflow' : 'B_or_C_legacy_analysis_plan',
176
- required_edges: scenario.required_edges,
177
- required_schemas: scenario.required_schemas,
178
- review_question: scenario.workflow
179
- ? `Does workflow ${scenario.workflow} need additional V6 source/target schemas or typed relations?`
180
- : `Is ${scenario.scope} still a valid SOP/business scenario, or a stale V4 planning artifact?`,
181
- decision: 'pending_review'
182
- }));
183
- }
184
-
185
- function buildSchemaPack(missingSchemas, v6Schemas) {
186
- return missingSchemas.map((schema) => {
187
- const match = findSchemaMatch(schema.schema_id, v6Schemas);
188
- const requiredScopes = uniq(schema.required_by.map((entry) => entry.scope));
189
- const sopFamilies = uniq(schema.required_by.map((entry) => entry.workflow ?? `legacy:${familyFromScope(entry.scope)}`));
190
- const decision = schemaDecision(schema.schema_id, match);
191
- return {
192
- schema_id: schema.schema_id,
193
- mece_bucket: schema.schema_id.startsWith('serenity-v4:')
194
- ? 'legacy_v4_name_mapping'
195
- : 'missing_or_ambiguous_v6_schema',
196
- domain: domainOfSchema(schema.schema_id),
197
- required_by_count: schema.required_by.length,
198
- required_by_scopes: requiredScopes,
199
- sop_families: sopFamilies,
200
- current_v6_equivalent: match.best,
201
- exact_v6_match: match.exact,
202
- field_gap: 'unknown_until_schema_inspection',
203
- relation_gap: 'unknown_until_relation_review',
204
- decision,
205
- canonical_v6_schema_id:
206
- decision === 'map_to_existing' || decision === 'review_possible_existing_mapping'
207
- ? match.best
208
- : `${TARGET_WORKSPACE}:${localSchemaId(schema.schema_id).replace(/^serenity-v4:/, '')}`,
209
- review_questions: [
210
- `Is ${schema.schema_id} a live SOP object or a stale V4 reference?`,
211
- 'Does the suggested V6 equivalent cover the same business meaning?',
212
- 'Which minimum fields are required by the SOP?',
213
- 'Which accepted relation should connect this schema to the existing V6 model?'
214
- ],
215
- next_action:
216
- decision === 'map_to_existing'
217
- ? 'update scenario/schema reference to existing V6 schema'
218
- : 'inspect SOP and decide add_schema vs rename vs reject'
219
- };
220
- });
221
- }
222
-
223
- function buildRelationPack(reviewRelations) {
224
- const grouped = new Map();
225
- for (const row of reviewRelations) {
226
- const current =
227
- grouped.get(row.relation_type) ?? {
228
- relation_type: row.relation_type,
229
- v4_instances: 0,
230
- workspaces: [],
231
- sources: new Set(),
232
- linkml_classes: new Set(),
233
- supported_scenarios: new Map()
234
- };
235
- current.v4_instances += Number(row.instance_count || 0);
236
- current.workspaces.push({ workspace_id: row.workspace_id, instance_count: row.instance_count });
237
- for (const source of splitSources(row.sources)) current.sources.add(source);
238
- for (const cls of splitSources(row.linkml_classes)) current.linkml_classes.add(cls);
239
- for (const scenario of row.supported_scenarios || []) current.supported_scenarios.set(scenario.scope, scenario);
240
- grouped.set(row.relation_type, current);
241
- }
242
-
243
- return [...grouped.values()]
244
- .sort((a, b) => a.relation_type.localeCompare(b.relation_type))
245
- .map((group) => {
246
- const scenarios = [...group.supported_scenarios.values()].sort((a, b) => a.scope.localeCompare(b.scope));
247
- const sources = [...group.sources].sort();
248
- const evidence = evidenceLevel(scenarios, sources);
249
- const family = relationFamily(group.relation_type);
250
- const decision = proposedRelationDecision(group.relation_type, evidence);
251
- return {
252
- relation_type: group.relation_type,
253
- mece_bucket: family,
254
- evidence_level: evidence,
255
- v4_instances: group.v4_instances,
256
- workspaces: group.workspaces.sort((a, b) => a.workspace_id.localeCompare(b.workspace_id)),
257
- sources,
258
- source_quality: sources.some((source) => /fake|smoke|mini_seed/i.test(source))
259
- ? 'mixed_or_generated'
260
- : 'structured_or_artifact',
261
- supported_scenarios: scenarios.map((scenario) => ({
262
- scope: scenario.scope,
263
- workflow: scenario.workflow,
264
- scenario_refs: scenario.scenario_refs
265
- })),
266
- linkml_class_sample: [...group.linkml_classes].sort().slice(0, 20),
267
- source_schema: null,
268
- target_schema: null,
269
- direction: 'unknown_until_sop_review',
270
- cardinality: 'unknown_until_sop_review',
271
- existing_v6_relation: null,
272
- canonical_v6_relation:
273
- decision === 'split_or_rename_required' ? null : suggestCanonicalRelation(group.relation_type),
274
- decision,
275
- devils_advocate_result:
276
- decision === 'split_or_rename_required'
277
- ? 'Fails as-is because the verb is too generic; split into SOP-specific relations.'
278
- : 'Pending source/target/direction proof.',
279
- review_questions: [
280
- `In which SOP is ${group.relation_type} required?`,
281
- 'What are the exact source and target schema types?',
282
- 'Can this be derived instead of stored?',
283
- 'Does V6 already express the same meaning with a better relation?',
284
- 'Would an operator understand this verb?'
285
- ],
286
- next_action:
287
- decision === 'split_or_rename_required'
288
- ? 'split into precise SOP-specific relations or reject'
289
- : 'write SOP sentence and source/target proposal'
290
- };
291
- });
292
- }
293
-
294
- function suggestCanonicalRelation(relationType) {
295
- const suggestions = {
296
- activer: 'activates',
297
- assigner_gestion: 'assigned_to_management_team',
298
- composer: 'composed_of',
299
- contenir: 'contains',
300
- declencher: 'triggers',
301
- donne_lieu_a: 'gives_rise_to',
302
- est_actee_par: 'recorded_by',
303
- est_calcule_a_partir_de: 'calculated_from',
304
- est_composee_d: 'composed_of',
305
- est_emise_par: 'issued_by',
306
- evalue: 'evaluates',
307
- fonde_une: 'grounds',
308
- peut_generer: 'may_generate',
309
- peut_preceder: 'may_precede',
310
- porte_sur: 'applies_to',
311
- produit_des: 'produces',
312
- rattacher: 'attached_to',
313
- resulte_de: 'results_from'
314
- };
315
- return suggestions[relationType] ?? relationType;
316
- }
317
-
318
- function buildRejectedAudit(rejectedRelations) {
319
- return rejectedRelations.map((row) => ({
320
- relation_type: row.relation_type,
321
- workspace_id: row.workspace_id,
322
- v4_instances: row.instance_count,
323
- rejection_reason: row.decision,
324
- source_quality: /fake|smoke|mini_seed/i.test(row.sources || '') ? 'generated_or_weak' : 'unmapped',
325
- sources: splitSources(row.sources),
326
- possible_sop_link: null,
327
- reopen_trigger:
328
- row.decision === 'reject_no_sop_or_scenario'
329
- ? 'reopen only if a concrete SOP sentence requires it'
330
- : row.decision === 'reject_generic_relation'
331
- ? 'reopen only as split/renamed specific relation'
332
- : 'keep rejected unless non-generated source is found',
333
- final_status: 'keep_rejected'
334
- }));
335
- }
336
-
337
- function writeMarkdown(summary, schemaPack, relationPack, rejectedAudit) {
338
- const byDecision = (items) =>
339
- items.reduce((acc, item) => {
340
- acc[item.decision] = (acc[item.decision] || 0) + 1;
341
- return acc;
342
- }, {});
343
- const rejectedByReason = rejectedAudit.reduce((acc, item) => {
344
- acc[item.rejection_reason] = (acc[item.rejection_reason] || 0) + 1;
345
- return acc;
346
- }, {});
347
- const md = `# Serenity Production V6 Review Pack
348
-
349
- This pack turns the raw V4/V4-S2 candidate reports into reviewable decision records.
350
-
351
- ## Summary
352
-
353
- | Area | Count |
354
- |---|---:|
355
- | Scenarios/SOP rows | ${summary.scenarios} |
356
- | Missing schema decisions | ${schemaPack.length} |
357
- | Relation decisions | ${relationPack.length} |
358
- | Rejected relation audit rows | ${rejectedAudit.length} |
359
-
360
- ## Schema Decisions
361
-
362
- \`\`\`json
363
- ${JSON.stringify(byDecision(schemaPack), null, 2)}
364
- \`\`\`
365
-
366
- ## Relation Decisions
367
-
368
- \`\`\`json
369
- ${JSON.stringify(byDecision(relationPack), null, 2)}
370
- \`\`\`
371
-
372
- ## Rejected Relation Audit
373
-
374
- \`\`\`json
375
- ${JSON.stringify(rejectedByReason, null, 2)}
376
- \`\`\`
377
-
378
- ## Review Rule
379
-
380
- A candidate can advance only when this sentence can be completed:
381
-
382
- > In SOP <name>, <source schema> must <relation> <target schema> so the operator can <decision/action>.
383
-
384
- ## Files
385
-
386
- - \`scenario_sop_review_matrix.json\`
387
- - \`schema_decision_pack.json\`
388
- - \`relation_decision_pack.json\`
389
- - \`rejected_relation_audit.json\`
390
- - \`review_questions.md\`
391
- `;
392
- writeFileSync(join(outDir, 'README.md'), md);
393
- }
394
-
395
- function writeQuestions(schemaPack, relationPack) {
396
- const topSchemas = schemaPack
397
- .slice()
398
- .sort((a, b) => b.required_by_count - a.required_by_count)
399
- .slice(0, 12)
400
- .map(
401
- (row) =>
402
- `- ${row.schema_id}: ${row.decision}; suggested=${row.current_v6_equivalent ?? row.canonical_v6_schema_id}; required_by=${row.required_by_count}`
403
- )
404
- .join('\n');
405
- const topRelations = relationPack
406
- .slice()
407
- .sort((a, b) => b.v4_instances - a.v4_instances)
408
- .slice(0, 12)
409
- .map(
410
- (row) =>
411
- `- ${row.relation_type}: ${row.decision}; bucket=${row.mece_bucket}; instances=${row.v4_instances}; scenarios=${row.supported_scenarios.length}`
412
- )
413
- .join('\n');
414
- const md = `# Review Questions
415
-
416
- ## Start Here: Schema Gaps
417
-
418
- ${topSchemas}
419
-
420
- For each: map to existing V6, add/rename, or reject as stale.
421
-
422
- ## Then: Relation Candidates
423
-
424
- ${topRelations}
425
-
426
- For each: write the SOP sentence, then decide promote renamed, derive only, split, or reject.
427
-
428
- ## Default Choices
429
-
430
- - Generic relation names are not promoted as-is.
431
- - Legacy no-workflow scenarios need SOP-family confirmation.
432
- - Mini-seed/fake/smoke evidence is not enough by itself.
433
- `;
434
- writeFileSync(join(outDir, 'review_questions.md'), md);
435
- }
436
-
437
- function main() {
438
- mkdirSync(outDir, { recursive: true });
439
- const scenarioSopMap = readJson('scenario_sop_map.json');
440
- const missingSchemas = readJson('missing_schemas_required_by_sop.json');
441
- const reviewRelations = readJson('review_required_relations.json');
442
- const rejectedRelations = readJson('rejected_relations.json');
443
- const v6Schemas = loadV6Schemas();
444
-
445
- const scenarioMatrix = buildScenarioMatrix(scenarioSopMap);
446
- const schemaPack = buildSchemaPack(missingSchemas, v6Schemas);
447
- const relationPack = buildRelationPack(reviewRelations);
448
- const rejectedAudit = buildRejectedAudit(rejectedRelations);
449
- const summary = {
450
- target_workspace: TARGET_WORKSPACE,
451
- scenarios: scenarioMatrix.length,
452
- missing_schema_cases: schemaPack.length,
453
- relation_cases: relationPack.length,
454
- rejected_relation_cases: rejectedAudit.length,
455
- policy: {
456
- auto_promote: false,
457
- default_unit: 'SOP/scenario',
458
- default_generic_relation_action: 'split_or_rename_required'
459
- }
460
- };
461
-
462
- writeJson('scenario_sop_review_matrix.json', scenarioMatrix);
463
- writeJson('schema_decision_pack.json', schemaPack);
464
- writeJson('relation_decision_pack.json', relationPack);
465
- writeJson('rejected_relation_audit.json', rejectedAudit);
466
- writeJson('review_pack_summary.json', summary);
467
- writeMarkdown(summary, schemaPack, relationPack, rejectedAudit);
468
- writeQuestions(schemaPack, relationPack);
469
-
470
- console.log(`wrote ${outDir}`);
471
- console.log(JSON.stringify(summary, null, 2));
472
- }
473
-
474
- try {
475
- main();
476
- } catch (error) {
477
- console.error(error instanceof Error ? error.message : error);
478
- process.exit(1);
479
- }