scene-capability-engine 3.0.8 → 3.2.0

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 (51) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/docs/331-poc-adaptation-roadmap.md +21 -2
  3. package/docs/331-poc-dual-track-integration-guide.md +10 -6
  4. package/docs/331-poc-weekly-delivery-checklist.md +5 -0
  5. package/docs/README.md +6 -0
  6. package/docs/command-reference.md +262 -4
  7. package/docs/handoff-profile-integration-guide.md +88 -0
  8. package/docs/interactive-customization/331-poc-sce-integration-checklist.md +148 -0
  9. package/docs/interactive-customization/README.md +362 -0
  10. package/docs/interactive-customization/adapter-extension-contract.md +55 -0
  11. package/docs/interactive-customization/adapter-extension-contract.sample.json +59 -0
  12. package/docs/interactive-customization/adapter-extension-contract.schema.json +192 -0
  13. package/docs/interactive-customization/approval-role-policy-baseline.json +36 -0
  14. package/docs/interactive-customization/change-intent.schema.json +72 -0
  15. package/docs/interactive-customization/change-plan.sample.json +41 -0
  16. package/docs/interactive-customization/change-plan.schema.json +125 -0
  17. package/docs/interactive-customization/cross-industry-replication-guide.md +49 -0
  18. package/docs/interactive-customization/dialogue-governance-policy-baseline.json +49 -0
  19. package/docs/interactive-customization/domain-pack-extension-flow.md +71 -0
  20. package/docs/interactive-customization/execution-record.schema.json +62 -0
  21. package/docs/interactive-customization/governance-alert-playbook.md +51 -0
  22. package/docs/interactive-customization/governance-report-template.md +46 -0
  23. package/docs/interactive-customization/governance-threshold-baseline.json +14 -0
  24. package/docs/interactive-customization/guardrail-policy-baseline.json +27 -0
  25. package/docs/interactive-customization/high-risk-action-catalog.json +22 -0
  26. package/docs/interactive-customization/moqui-adapter-interface.md +40 -0
  27. package/docs/interactive-customization/moqui-context-provider.sample.json +72 -0
  28. package/docs/interactive-customization/moqui-copilot-context-contract.json +50 -0
  29. package/docs/interactive-customization/moqui-copilot-integration-guide.md +100 -0
  30. package/docs/interactive-customization/moqui-interactive-template-playbook.md +94 -0
  31. package/docs/interactive-customization/non-technical-usability-report.md +57 -0
  32. package/docs/interactive-customization/page-context.sample.json +73 -0
  33. package/docs/interactive-customization/page-context.schema.json +150 -0
  34. package/docs/interactive-customization/phase-acceptance-evidence.md +110 -0
  35. package/docs/interactive-customization/runtime-mode-policy-baseline.json +99 -0
  36. package/docs/moqui-template-core-library-playbook.md +28 -0
  37. package/docs/release-checklist.md +29 -4
  38. package/docs/security-governance-default-baseline.md +54 -0
  39. package/docs/starter-kit/README.md +50 -0
  40. package/docs/starter-kit/handoff-manifest.starter.json +32 -0
  41. package/docs/starter-kit/handoff-profile-ci.sample.yml +53 -0
  42. package/docs/starter-kit/release.workflow.sample.yml +41 -0
  43. package/docs/zh/README.md +12 -0
  44. package/lib/auto/moqui-recovery-sequence.js +62 -0
  45. package/lib/commands/auto.js +245 -34
  46. package/lib/commands/scene.js +867 -0
  47. package/lib/data/moqui-capability-lexicon.json +14 -1
  48. package/lib/interactive-customization/change-plan-gate-core.js +201 -0
  49. package/lib/interactive-customization/index.js +9 -0
  50. package/lib/interactive-customization/moqui-interactive-adapter.js +732 -0
  51. package/package.json +27 -2
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2026.02.18.1",
2
+ "version": "2026.02.19.2",
3
3
  "source": "sce-moqui-core",
4
4
  "capabilities": [
5
5
  {
@@ -244,6 +244,19 @@
244
244
  "workflow-approval-center"
245
245
  ]
246
246
  },
247
+ {
248
+ "canonical": "platform-interactive-customization-loop",
249
+ "aliases": [
250
+ "interactive-customization-loop",
251
+ "interactive-change-loop",
252
+ "moqui-interactive-customization-loop",
253
+ "scene.moqui.interactive.customization.loop",
254
+ "scene-moqui-interactive-customization-loop"
255
+ ],
256
+ "deprecated_aliases": [
257
+ "interactive-customization-pipeline"
258
+ ]
259
+ },
247
260
  {
248
261
  "canonical": "platform-reporting-audit-ops",
249
262
  "aliases": [
@@ -0,0 +1,201 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_POLICY = 'docs/interactive-customization/guardrail-policy-baseline.json';
4
+ const DEFAULT_CATALOG = 'docs/interactive-customization/high-risk-action-catalog.json';
5
+
6
+ function toUniqueList(value) {
7
+ if (!Array.isArray(value)) {
8
+ return [];
9
+ }
10
+ return Array.from(new Set(
11
+ value
12
+ .map(item => `${item || ''}`.trim())
13
+ .filter(Boolean)
14
+ ));
15
+ }
16
+
17
+ function normalizeRiskLevel(value) {
18
+ const risk = `${value || ''}`.trim().toLowerCase();
19
+ return ['low', 'medium', 'high'].includes(risk) ? risk : null;
20
+ }
21
+
22
+ function buildCheck(id, passed, severity, details) {
23
+ return {
24
+ id,
25
+ passed: passed === true,
26
+ severity,
27
+ details: details || null
28
+ };
29
+ }
30
+
31
+ function evaluatePlanGate(plan, policy, catalog) {
32
+ const checks = [];
33
+ const actions = Array.isArray(plan && plan.actions) ? plan.actions.filter(item => item && typeof item === 'object') : [];
34
+ const riskLevel = normalizeRiskLevel(plan && plan.risk_level);
35
+ const approval = plan && plan.approval && typeof plan.approval === 'object' ? plan.approval : {};
36
+ const security = plan && plan.security && typeof plan.security === 'object' ? plan.security : {};
37
+
38
+ const planShapePassed = (
39
+ Boolean(plan && typeof plan.plan_id === 'string' && plan.plan_id.trim().length > 0) &&
40
+ Boolean(plan && typeof plan.intent_id === 'string' && plan.intent_id.trim().length > 0) &&
41
+ Boolean(riskLevel) &&
42
+ actions.length > 0
43
+ );
44
+ checks.push(buildCheck(
45
+ 'plan-shape',
46
+ planShapePassed,
47
+ 'deny',
48
+ planShapePassed ? null : 'plan_id/intent_id/risk_level/actions is invalid or missing'
49
+ ));
50
+
51
+ const policyApproval = policy && policy.approval_policy && typeof policy.approval_policy === 'object'
52
+ ? policy.approval_policy
53
+ : {};
54
+ const policySecurity = policy && policy.security_policy && typeof policy.security_policy === 'object'
55
+ ? policy.security_policy
56
+ : {};
57
+ const catalogData = catalog && catalog.catalog && typeof catalog.catalog === 'object'
58
+ ? catalog.catalog
59
+ : {};
60
+
61
+ const denyActionTypes = new Set(toUniqueList(catalogData.deny_action_types));
62
+ const reviewActionTypes = new Set(toUniqueList(catalogData.review_required_action_types));
63
+
64
+ const deniedActionHits = actions
65
+ .map(item => `${item.type || ''}`.trim())
66
+ .filter(type => type && denyActionTypes.has(type));
67
+ checks.push(buildCheck(
68
+ 'deny-action-types',
69
+ deniedActionHits.length === 0,
70
+ 'deny',
71
+ deniedActionHits.length > 0
72
+ ? `blocked action types: ${Array.from(new Set(deniedActionHits)).join(', ')}`
73
+ : null
74
+ ));
75
+
76
+ const reviewActionHits = actions
77
+ .map(item => `${item.type || ''}`.trim())
78
+ .filter(type => type && reviewActionTypes.has(type));
79
+ const reviewActionApproved = reviewActionHits.length === 0 || approval.status === 'approved';
80
+ checks.push(buildCheck(
81
+ 'review-action-types',
82
+ reviewActionApproved,
83
+ 'review',
84
+ reviewActionApproved
85
+ ? null
86
+ : `review-required action types without approval: ${Array.from(new Set(reviewActionHits)).join(', ')}`
87
+ ));
88
+
89
+ const approvalRiskLevels = new Set(
90
+ toUniqueList(policyApproval.require_approval_for_risk_levels).map(item => item.toLowerCase())
91
+ );
92
+ const riskApprovalPassed = !approvalRiskLevels.has(riskLevel || '') || approval.status === 'approved';
93
+ checks.push(buildCheck(
94
+ 'risk-approval',
95
+ riskApprovalPassed,
96
+ 'review',
97
+ riskApprovalPassed
98
+ ? null
99
+ : `risk level ${riskLevel} requires approval`
100
+ ));
101
+
102
+ const maxActionsWithoutApproval = Number(policyApproval.max_actions_without_approval);
103
+ const actionCountApprovalPassed = (
104
+ !Number.isFinite(maxActionsWithoutApproval) ||
105
+ maxActionsWithoutApproval < 0 ||
106
+ actions.length <= maxActionsWithoutApproval ||
107
+ approval.status === 'approved'
108
+ );
109
+ checks.push(buildCheck(
110
+ 'action-count-approval',
111
+ actionCountApprovalPassed,
112
+ 'review',
113
+ actionCountApprovalPassed
114
+ ? null
115
+ : `action count ${actions.length} exceeds max_actions_without_approval ${maxActionsWithoutApproval}`
116
+ ));
117
+
118
+ const privilegeEscalation = actions.some(item => item && item.requires_privilege_escalation === true);
119
+ const requireDualApproval = policyApproval.require_dual_approval_for_privilege_escalation === true;
120
+ const dualApprovalPassed = !privilegeEscalation || !requireDualApproval || approval.dual_approved === true;
121
+ checks.push(buildCheck(
122
+ 'privilege-escalation-dual-approval',
123
+ dualApprovalPassed,
124
+ 'review',
125
+ dualApprovalPassed
126
+ ? null
127
+ : 'privilege escalation detected without dual approval'
128
+ ));
129
+
130
+ const touchesSensitiveData = actions.some(item => item && item.touches_sensitive_data === true);
131
+ const requireMasking = policySecurity.require_masking_when_sensitive_data === true;
132
+ const maskingPassed = !touchesSensitiveData || !requireMasking || security.masking_applied === true;
133
+ checks.push(buildCheck(
134
+ 'sensitive-data-masking',
135
+ maskingPassed,
136
+ 'deny',
137
+ maskingPassed
138
+ ? null
139
+ : 'sensitive data change requires masking_applied=true'
140
+ ));
141
+
142
+ const forbidPlaintextSecrets = policySecurity.forbid_plaintext_secrets === true;
143
+ const plaintextSecretsPassed = !forbidPlaintextSecrets || security.plaintext_secrets_in_payload !== true;
144
+ checks.push(buildCheck(
145
+ 'plaintext-secrets',
146
+ plaintextSecretsPassed,
147
+ 'deny',
148
+ plaintextSecretsPassed
149
+ ? null
150
+ : 'plaintext secrets detected in plan payload'
151
+ ));
152
+
153
+ const hasIrreversibleAction = actions.some(item => item && item.irreversible === true);
154
+ const requireBackup = policySecurity.require_backup_for_irreversible_actions === true;
155
+ const backupPassed = !hasIrreversibleAction || !requireBackup || Boolean(`${security.backup_reference || ''}`.trim());
156
+ checks.push(buildCheck(
157
+ 'irreversible-backup',
158
+ backupPassed,
159
+ 'deny',
160
+ backupPassed
161
+ ? null
162
+ : 'irreversible actions require backup_reference'
163
+ ));
164
+
165
+ const failedDenyChecks = checks.filter(item => item.passed !== true && item.severity === 'deny');
166
+ const failedReviewChecks = checks.filter(item => item.passed !== true && item.severity === 'review');
167
+
168
+ let decision = 'allow';
169
+ if (failedDenyChecks.length > 0) {
170
+ decision = 'deny';
171
+ } else if (failedReviewChecks.length > 0) {
172
+ decision = 'review-required';
173
+ }
174
+
175
+ return {
176
+ decision,
177
+ checks,
178
+ failed_deny_checks: failedDenyChecks.map(item => item.id),
179
+ failed_review_checks: failedReviewChecks.map(item => item.id),
180
+ reasons: checks
181
+ .filter(item => item.passed !== true && item.details)
182
+ .map(item => item.details),
183
+ summary: {
184
+ check_total: checks.length,
185
+ failed_total: checks.filter(item => item.passed !== true).length,
186
+ failed_deny_total: failedDenyChecks.length,
187
+ failed_review_total: failedReviewChecks.length,
188
+ action_count: actions.length,
189
+ risk_level: riskLevel
190
+ }
191
+ };
192
+ }
193
+
194
+ module.exports = {
195
+ DEFAULT_POLICY,
196
+ DEFAULT_CATALOG,
197
+ toUniqueList,
198
+ normalizeRiskLevel,
199
+ buildCheck,
200
+ evaluatePlanGate
201
+ };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const moquiAdapter = require('./moqui-interactive-adapter');
4
+ const changePlanGateCore = require('./change-plan-gate-core');
5
+
6
+ module.exports = {
7
+ ...changePlanGateCore,
8
+ ...moquiAdapter
9
+ };