scene-capability-engine 3.6.45 → 3.6.46

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 (56) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/docs/releases/README.md +1 -0
  3. package/docs/releases/v3.6.46.md +23 -0
  4. package/docs/zh/releases/README.md +1 -0
  5. package/docs/zh/releases/v3.6.46.md +23 -0
  6. package/package.json +4 -2
  7. package/scripts/auto-strategy-router.js +231 -0
  8. package/scripts/capability-mapping-report.js +339 -0
  9. package/scripts/check-branding-consistency.js +140 -0
  10. package/scripts/check-sce-tracking.js +54 -0
  11. package/scripts/check-skip-allowlist.js +94 -0
  12. package/scripts/errorbook-registry-health-gate.js +172 -0
  13. package/scripts/errorbook-release-gate.js +132 -0
  14. package/scripts/failure-attribution-repair.js +317 -0
  15. package/scripts/git-managed-gate.js +464 -0
  16. package/scripts/interactive-approval-event-projection.js +400 -0
  17. package/scripts/interactive-approval-workflow.js +829 -0
  18. package/scripts/interactive-authorization-tier-evaluate.js +413 -0
  19. package/scripts/interactive-change-plan-gate.js +225 -0
  20. package/scripts/interactive-context-bridge.js +617 -0
  21. package/scripts/interactive-customization-loop.js +1690 -0
  22. package/scripts/interactive-dialogue-governance.js +842 -0
  23. package/scripts/interactive-feedback-log.js +253 -0
  24. package/scripts/interactive-flow-smoke.js +238 -0
  25. package/scripts/interactive-flow.js +1059 -0
  26. package/scripts/interactive-governance-report.js +1112 -0
  27. package/scripts/interactive-intent-build.js +707 -0
  28. package/scripts/interactive-loop-smoke.js +215 -0
  29. package/scripts/interactive-moqui-adapter.js +304 -0
  30. package/scripts/interactive-plan-build.js +426 -0
  31. package/scripts/interactive-runtime-policy-evaluate.js +495 -0
  32. package/scripts/interactive-work-order-build.js +552 -0
  33. package/scripts/matrix-regression-gate.js +167 -0
  34. package/scripts/moqui-core-regression-suite.js +397 -0
  35. package/scripts/moqui-lexicon-audit.js +651 -0
  36. package/scripts/moqui-matrix-remediation-phased-runner.js +865 -0
  37. package/scripts/moqui-matrix-remediation-queue.js +852 -0
  38. package/scripts/moqui-metadata-extract.js +1340 -0
  39. package/scripts/moqui-rebuild-gate.js +167 -0
  40. package/scripts/moqui-release-summary.js +729 -0
  41. package/scripts/moqui-standard-rebuild.js +1370 -0
  42. package/scripts/moqui-template-baseline-report.js +682 -0
  43. package/scripts/npm-package-runtime-asset-check.js +221 -0
  44. package/scripts/problem-closure-gate.js +441 -0
  45. package/scripts/release-asset-integrity-check.js +216 -0
  46. package/scripts/release-asset-nonempty-normalize.js +166 -0
  47. package/scripts/release-drift-evaluate.js +223 -0
  48. package/scripts/release-drift-signals.js +255 -0
  49. package/scripts/release-governance-snapshot-export.js +132 -0
  50. package/scripts/release-ops-weekly-summary.js +934 -0
  51. package/scripts/release-risk-remediation-bundle.js +315 -0
  52. package/scripts/release-weekly-ops-gate.js +423 -0
  53. package/scripts/state-migration-reconciliation-gate.js +110 -0
  54. package/scripts/state-storage-tiering-audit.js +337 -0
  55. package/scripts/steering-content-audit.js +393 -0
  56. package/scripts/symbol-evidence-locate.js +366 -0
@@ -0,0 +1,426 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('path');
5
+ const fs = require('fs-extra');
6
+ const crypto = require('crypto');
7
+
8
+ const DEFAULT_OUT_PLAN = '.sce/reports/interactive-change-plan.generated.json';
9
+ const DEFAULT_OUT_MARKDOWN = '.sce/reports/interactive-change-plan.generated.md';
10
+ const DEFAULT_AUTH_PASSWORD_HASH_ENV = 'SCE_INTERACTIVE_AUTH_PASSWORD_SHA256';
11
+ const DEFAULT_AUTH_PASSWORD_TTL_SECONDS = 600;
12
+
13
+ function parseArgs(argv) {
14
+ const options = {
15
+ intent: null,
16
+ context: null,
17
+ executionMode: 'suggestion',
18
+ outPlan: DEFAULT_OUT_PLAN,
19
+ outMarkdown: DEFAULT_OUT_MARKDOWN,
20
+ json: false
21
+ };
22
+
23
+ for (let i = 0; i < argv.length; i += 1) {
24
+ const token = argv[i];
25
+ const next = argv[i + 1];
26
+ if (token === '--intent' && next) {
27
+ options.intent = next;
28
+ i += 1;
29
+ } else if (token === '--context' && next) {
30
+ options.context = next;
31
+ i += 1;
32
+ } else if (token === '--execution-mode' && next) {
33
+ options.executionMode = next;
34
+ i += 1;
35
+ } else if (token === '--out-plan' && next) {
36
+ options.outPlan = next;
37
+ i += 1;
38
+ } else if (token === '--out-markdown' && next) {
39
+ options.outMarkdown = next;
40
+ i += 1;
41
+ } else if (token === '--json') {
42
+ options.json = true;
43
+ } else if (token === '--help' || token === '-h') {
44
+ printHelpAndExit(0);
45
+ }
46
+ }
47
+
48
+ if (!options.intent) {
49
+ throw new Error('--intent is required.');
50
+ }
51
+ if (!['suggestion', 'apply'].includes(`${options.executionMode || ''}`.trim())) {
52
+ throw new Error('--execution-mode must be one of: suggestion, apply');
53
+ }
54
+ return options;
55
+ }
56
+
57
+ function printHelpAndExit(code) {
58
+ const lines = [
59
+ 'Usage: node scripts/interactive-plan-build.js --intent <path> [options]',
60
+ '',
61
+ 'Options:',
62
+ ' --intent <path> Change intent JSON file (required)',
63
+ ' --context <path> Optional page context JSON file',
64
+ ' --execution-mode <mode> suggestion|apply (default: suggestion)',
65
+ ` --out-plan <path> Plan JSON output path (default: ${DEFAULT_OUT_PLAN})`,
66
+ ` --out-markdown <path> Plan markdown output path (default: ${DEFAULT_OUT_MARKDOWN})`,
67
+ ' --json Print generated payload to stdout',
68
+ ' -h, --help Show this help'
69
+ ];
70
+ console.log(lines.join('\n'));
71
+ process.exit(code);
72
+ }
73
+
74
+ function resolveFile(cwd, value) {
75
+ return path.isAbsolute(value) ? value : path.resolve(cwd, value);
76
+ }
77
+
78
+ async function readJsonFile(filePath, label) {
79
+ if (!(await fs.pathExists(filePath))) {
80
+ throw new Error(`${label} not found: ${filePath}`);
81
+ }
82
+ const text = await fs.readFile(filePath, 'utf8');
83
+ try {
84
+ return JSON.parse(text);
85
+ } catch (error) {
86
+ throw new Error(`invalid JSON in ${label}: ${error.message}`);
87
+ }
88
+ }
89
+
90
+ function normalizeText(value) {
91
+ return `${value || ''}`.trim();
92
+ }
93
+
94
+ function inferActionTypes(intent, context) {
95
+ const goalText = normalizeText(intent && intent.business_goal).toLowerCase();
96
+ const moduleText = normalizeText(context && context.module).toLowerCase();
97
+ const entityText = normalizeText(context && context.entity).toLowerCase();
98
+ const merged = `${goalText} ${moduleText} ${entityText}`;
99
+
100
+ const actionTypes = [];
101
+ const pushType = (value) => {
102
+ if (!actionTypes.includes(value)) {
103
+ actionTypes.push(value);
104
+ }
105
+ };
106
+
107
+ if (/(approval|workflow|escalation|review)/.test(merged)) {
108
+ pushType('workflow_approval_chain_change');
109
+ }
110
+ if (/(rule|threshold|policy|strategy|decision)/.test(merged)) {
111
+ pushType('update_rule_threshold');
112
+ }
113
+ if (/(field|form|layout|ui|screen|page)/.test(merged)) {
114
+ pushType('ui_form_field_adjust');
115
+ }
116
+ if (/(inventory|stock|warehouse|reserve)/.test(merged)) {
117
+ pushType('inventory_adjustment_bulk');
118
+ }
119
+ if (/(payment|refund|billing)/.test(merged)) {
120
+ pushType('payment_rule_change');
121
+ }
122
+ if (/(delete|drop|remove all|truncate)/.test(merged)) {
123
+ pushType('bulk_delete_without_filter');
124
+ }
125
+ if (/(permission|privilege|admin|role)/.test(merged)) {
126
+ pushType('permission_grant_super_admin');
127
+ }
128
+ if (/(token|secret|credential|apikey|password)/.test(merged)) {
129
+ pushType('credential_export');
130
+ }
131
+
132
+ if (actionTypes.length === 0) {
133
+ pushType('analysis_only');
134
+ }
135
+ return actionTypes;
136
+ }
137
+
138
+ function actionTemplate(type, index) {
139
+ const base = {
140
+ action_id: `act-${String(index + 1).padStart(3, '0')}`,
141
+ type,
142
+ touches_sensitive_data: false,
143
+ requires_privilege_escalation: false,
144
+ irreversible: false
145
+ };
146
+
147
+ if (['payment_rule_change', 'credential_export'].includes(type)) {
148
+ base.touches_sensitive_data = true;
149
+ }
150
+ if (['permission_grant_super_admin'].includes(type)) {
151
+ base.requires_privilege_escalation = true;
152
+ }
153
+ if (['bulk_delete_without_filter', 'raw_sql_destructive'].includes(type)) {
154
+ base.irreversible = true;
155
+ }
156
+ return base;
157
+ }
158
+
159
+ function inferRiskLevel(actions, intent, context) {
160
+ const goalText = normalizeText(intent && intent.business_goal).toLowerCase();
161
+ const actionTypes = actions.map(item => item.type);
162
+ if (
163
+ actionTypes.some(type => [
164
+ 'credential_export',
165
+ 'permission_grant_super_admin',
166
+ 'bulk_delete_without_filter'
167
+ ].includes(type)) ||
168
+ /(delete|privilege|token|secret|credential)/.test(goalText)
169
+ ) {
170
+ return 'high';
171
+ }
172
+ if (
173
+ actionTypes.some(type => [
174
+ 'workflow_approval_chain_change',
175
+ 'payment_rule_change',
176
+ 'inventory_adjustment_bulk'
177
+ ].includes(type)) ||
178
+ /(approval|workflow|payment|inventory|refund)/.test(goalText) ||
179
+ /(payment|inventory)/.test(normalizeText(context && context.module).toLowerCase())
180
+ ) {
181
+ return 'medium';
182
+ }
183
+ return 'low';
184
+ }
185
+
186
+ function buildVerificationChecks(actions) {
187
+ const checks = [];
188
+ const pushCheck = (value) => {
189
+ if (!checks.includes(value)) {
190
+ checks.push(value);
191
+ }
192
+ };
193
+
194
+ pushCheck('intent-to-plan consistency review');
195
+ actions.forEach(action => {
196
+ switch (action.type) {
197
+ case 'workflow_approval_chain_change':
198
+ pushCheck('approval workflow regression smoke');
199
+ pushCheck('approval escalation path validation');
200
+ break;
201
+ case 'update_rule_threshold':
202
+ pushCheck('rule threshold snapshot compare');
203
+ break;
204
+ case 'ui_form_field_adjust':
205
+ pushCheck('ui field rendering smoke');
206
+ break;
207
+ case 'inventory_adjustment_bulk':
208
+ pushCheck('inventory non-negative invariant check');
209
+ break;
210
+ case 'payment_rule_change':
211
+ pushCheck('payment authorization regression smoke');
212
+ break;
213
+ case 'bulk_delete_without_filter':
214
+ pushCheck('destructive action simulation in dry-run');
215
+ break;
216
+ default:
217
+ pushCheck('general change smoke validation');
218
+ break;
219
+ }
220
+ });
221
+ return checks;
222
+ }
223
+
224
+ function summarizeImpact(actions, context) {
225
+ const actionTypes = actions.map(item => item.type);
226
+ return {
227
+ business: actionTypes.length === 1 && actionTypes[0] === 'analysis_only'
228
+ ? 'analysis only, no direct business mutation proposed'
229
+ : `potential impact on ${normalizeText(context && context.module) || 'business module'} operations`,
230
+ technical: `generated ${actions.length} action(s): ${actionTypes.join(', ')}`,
231
+ data: actions.some(item => item.touches_sensitive_data)
232
+ ? 'sensitive data path involved; masking and approval required'
233
+ : 'no explicit sensitive data write path detected'
234
+ };
235
+ }
236
+
237
+ function buildRollbackPlan(actions) {
238
+ const irreversible = actions.some(item => item.irreversible);
239
+ return {
240
+ type: irreversible ? 'backup-restore' : 'config-revert',
241
+ reference: irreversible
242
+ ? `backup-required-${new Date().toISOString().slice(0, 10)}`
243
+ : 'previous-config-snapshot',
244
+ note: irreversible
245
+ ? 'irreversible action detected; verified backup is mandatory before apply'
246
+ : 'revert to previous rule/config snapshot'
247
+ };
248
+ }
249
+
250
+ function buildApproval(riskLevel, executionMode, actions) {
251
+ const hasPrivilegeEscalation = actions.some(item => item.requires_privilege_escalation);
252
+ const mustApprove = (
253
+ riskLevel === 'high' ||
254
+ (riskLevel === 'medium' && executionMode === 'apply') ||
255
+ hasPrivilegeEscalation
256
+ );
257
+ return {
258
+ status: mustApprove ? 'pending' : 'not-required',
259
+ dual_approved: false,
260
+ approvers: []
261
+ };
262
+ }
263
+
264
+ function isMutatingAction(actionType) {
265
+ return `${actionType || ''}`.trim().toLowerCase() !== 'analysis_only';
266
+ }
267
+
268
+ function buildAuthorization(actions, executionMode, riskLevel) {
269
+ const mutating = actions.some(action => isMutatingAction(action.type));
270
+ const requiresPrivilegeEscalation = actions.some(action => action.requires_privilege_escalation === true);
271
+ const requiresPassword = mutating && `${executionMode || ''}`.trim().toLowerCase() === 'apply';
272
+ const reasonCodes = [];
273
+
274
+ if (requiresPassword) {
275
+ reasonCodes.push('mutating-action-apply-mode');
276
+ }
277
+ if (requiresPrivilegeEscalation) {
278
+ reasonCodes.push('privilege-escalation-detected');
279
+ }
280
+ if (`${riskLevel || ''}`.trim().toLowerCase() === 'high') {
281
+ reasonCodes.push('high-risk-plan');
282
+ }
283
+
284
+ return {
285
+ password_required: requiresPassword,
286
+ password_scope: requiresPassword ? ['execute'] : [],
287
+ password_hash_env: DEFAULT_AUTH_PASSWORD_HASH_ENV,
288
+ password_ttl_seconds: DEFAULT_AUTH_PASSWORD_TTL_SECONDS,
289
+ reason_codes: reasonCodes
290
+ };
291
+ }
292
+
293
+ function buildMarkdown(payload) {
294
+ const plan = payload.plan;
295
+ const lines = [];
296
+ lines.push('# Interactive Change Plan (Generated)');
297
+ lines.push('');
298
+ lines.push(`- Generated at: ${payload.generated_at}`);
299
+ lines.push(`- Intent ID: ${plan.intent_id}`);
300
+ lines.push(`- Plan ID: ${plan.plan_id}`);
301
+ lines.push(`- Risk level: ${plan.risk_level}`);
302
+ lines.push(`- Execution mode: ${plan.execution_mode}`);
303
+ lines.push(`- Approval status: ${plan.approval.status}`);
304
+ lines.push(`- Password authorization required: ${plan.authorization.password_required ? 'yes' : 'no'}`);
305
+ lines.push('');
306
+ lines.push('## Scope');
307
+ lines.push('');
308
+ lines.push(`- Product: ${plan.scope.product || 'n/a'}`);
309
+ lines.push(`- Module: ${plan.scope.module || 'n/a'}`);
310
+ lines.push(`- Page: ${plan.scope.page || 'n/a'}`);
311
+ lines.push(`- Entity: ${plan.scope.entity || 'n/a'}`);
312
+ lines.push(`- Scene: ${plan.scope.scene_id || 'n/a'}`);
313
+ lines.push('');
314
+ lines.push('## Actions');
315
+ lines.push('');
316
+ lines.push('| Action ID | Type | Sensitive | Privilege Escalation | Irreversible |');
317
+ lines.push('| --- | --- | --- | --- | --- |');
318
+ for (const action of plan.actions) {
319
+ lines.push(`| ${action.action_id} | ${action.type} | ${action.touches_sensitive_data ? 'yes' : 'no'} | ${action.requires_privilege_escalation ? 'yes' : 'no'} | ${action.irreversible ? 'yes' : 'no'} |`);
320
+ }
321
+ lines.push('');
322
+ lines.push('## Verification');
323
+ lines.push('');
324
+ plan.verification_checks.forEach(item => lines.push(`- ${item}`));
325
+ lines.push('');
326
+ lines.push('## Rollback');
327
+ lines.push('');
328
+ lines.push(`- Type: ${plan.rollback_plan.type}`);
329
+ lines.push(`- Reference: ${plan.rollback_plan.reference}`);
330
+ lines.push(`- Note: ${plan.rollback_plan.note}`);
331
+ lines.push('');
332
+ lines.push('## Authorization');
333
+ lines.push('');
334
+ lines.push(`- Password required: ${plan.authorization.password_required ? 'yes' : 'no'}`);
335
+ lines.push(`- Password scope: ${Array.isArray(plan.authorization.password_scope) && plan.authorization.password_scope.length > 0 ? plan.authorization.password_scope.join(', ') : 'none'}`);
336
+ lines.push(`- Password hash env: ${plan.authorization.password_hash_env || 'n/a'}`);
337
+ lines.push(`- Password TTL (seconds): ${plan.authorization.password_ttl_seconds || 'n/a'}`);
338
+ if (Array.isArray(plan.authorization.reason_codes) && plan.authorization.reason_codes.length > 0) {
339
+ lines.push(`- Reason codes: ${plan.authorization.reason_codes.join(', ')}`);
340
+ }
341
+ lines.push('');
342
+ lines.push('## Gate Command');
343
+ lines.push('');
344
+ lines.push(`- ${payload.gate_hint_command}`);
345
+ return `${lines.join('\n')}\n`;
346
+ }
347
+
348
+ async function main() {
349
+ const options = parseArgs(process.argv.slice(2));
350
+ const cwd = process.cwd();
351
+ const intentPath = resolveFile(cwd, options.intent);
352
+ const contextPath = options.context ? resolveFile(cwd, options.context) : null;
353
+ const outPlanPath = resolveFile(cwd, options.outPlan);
354
+ const outMarkdownPath = resolveFile(cwd, options.outMarkdown);
355
+
356
+ const intent = await readJsonFile(intentPath, 'intent');
357
+ const context = contextPath ? await readJsonFile(contextPath, 'context') : (intent.context_ref || {});
358
+
359
+ const actionTypes = inferActionTypes(intent, context);
360
+ const actions = actionTypes.map((type, index) => actionTemplate(type, index));
361
+ const riskLevel = inferRiskLevel(actions, intent, context);
362
+ const verificationChecks = buildVerificationChecks(actions);
363
+ const rollbackPlan = buildRollbackPlan(actions);
364
+ const approval = buildApproval(riskLevel, options.executionMode, actions);
365
+ const authorization = buildAuthorization(actions, options.executionMode, riskLevel);
366
+ const security = {
367
+ masking_applied: actions.some(item => item.touches_sensitive_data),
368
+ plaintext_secrets_in_payload: false,
369
+ backup_reference: rollbackPlan.type === 'backup-restore' ? rollbackPlan.reference : undefined
370
+ };
371
+
372
+ const plan = {
373
+ plan_id: `plan-${crypto.randomUUID()}`,
374
+ intent_id: intent.intent_id,
375
+ risk_level: riskLevel,
376
+ execution_mode: options.executionMode,
377
+ scope: {
378
+ product: context.product || intent.context_ref && intent.context_ref.product || null,
379
+ module: context.module || intent.context_ref && intent.context_ref.module || null,
380
+ page: context.page || intent.context_ref && intent.context_ref.page || null,
381
+ entity: context.entity || intent.context_ref && intent.context_ref.entity || null,
382
+ scene_id: context.scene_id || intent.context_ref && intent.context_ref.scene_id || null
383
+ },
384
+ actions,
385
+ impact_assessment: summarizeImpact(actions, context),
386
+ verification_checks: verificationChecks,
387
+ rollback_plan: rollbackPlan,
388
+ approval,
389
+ authorization,
390
+ security,
391
+ created_at: new Date().toISOString()
392
+ };
393
+
394
+ const payload = {
395
+ mode: 'interactive-plan-build',
396
+ generated_at: plan.created_at,
397
+ input: {
398
+ intent: path.relative(cwd, intentPath) || '.',
399
+ context: contextPath ? (path.relative(cwd, contextPath) || '.') : null
400
+ },
401
+ plan,
402
+ gate_hint_command: `node scripts/interactive-change-plan-gate.js --plan ${path.relative(cwd, outPlanPath) || '.'} --json`,
403
+ output: {
404
+ plan: path.relative(cwd, outPlanPath) || '.',
405
+ markdown: path.relative(cwd, outMarkdownPath) || '.'
406
+ }
407
+ };
408
+
409
+ await fs.ensureDir(path.dirname(outPlanPath));
410
+ await fs.writeJson(outPlanPath, plan, { spaces: 2 });
411
+ await fs.ensureDir(path.dirname(outMarkdownPath));
412
+ await fs.writeFile(outMarkdownPath, buildMarkdown(payload), 'utf8');
413
+
414
+ if (options.json) {
415
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
416
+ } else {
417
+ process.stdout.write('Interactive change plan generated.\n');
418
+ process.stdout.write(`- Plan: ${payload.output.plan}\n`);
419
+ process.stdout.write(`- Markdown: ${payload.output.markdown}\n`);
420
+ }
421
+ }
422
+
423
+ main().catch((error) => {
424
+ console.error(`Interactive plan build failed: ${error.message}`);
425
+ process.exit(1);
426
+ });