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,1690 @@
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
+ const { spawnSync } = require('child_process');
8
+ const {
9
+ DEFAULT_BUSINESS_MODE_POLICY,
10
+ BUSINESS_MODES,
11
+ normalizeBusinessMode,
12
+ applyBusinessModePolicy
13
+ } = require('../lib/runtime/business-mode-resolver');
14
+
15
+ const DEFAULT_OUT_DIR = '.sce/reports/interactive-loop';
16
+ const DEFAULT_USER_ID = 'anonymous-user';
17
+ const DEFAULT_APPROVAL_ACTOR = 'workflow-operator';
18
+ const DEFAULT_FEEDBACK_CHANNEL = 'ui';
19
+ const DEFAULT_AUTH_PASSWORD_HASH_ENV = 'SCE_INTERACTIVE_AUTH_PASSWORD_SHA256';
20
+ const DEFAULT_RUNTIME_MODE = 'ops-fix';
21
+ const DEFAULT_RUNTIME_ENVIRONMENT = 'staging';
22
+ const DEFAULT_DIALOGUE_PROFILE = 'business-user';
23
+ const FEEDBACK_CHANNELS = new Set(['ui', 'cli', 'api', 'other']);
24
+ const RUNTIME_MODES = new Set(['user-assist', 'ops-fix', 'feature-dev']);
25
+ const RUNTIME_ENVIRONMENTS = new Set(['dev', 'staging', 'prod']);
26
+ const DIALOGUE_PROFILES = new Set(['business-user', 'system-maintainer']);
27
+ const UI_MODES = new Set(['user-app', 'ops-console']);
28
+
29
+ const SCRIPT_DIALOGUE = path.resolve(__dirname, 'interactive-dialogue-governance.js');
30
+ const SCRIPT_INTENT = path.resolve(__dirname, 'interactive-intent-build.js');
31
+ const SCRIPT_PLAN = path.resolve(__dirname, 'interactive-plan-build.js');
32
+ const SCRIPT_GATE = path.resolve(__dirname, 'interactive-change-plan-gate.js');
33
+ const SCRIPT_RUNTIME = path.resolve(__dirname, 'interactive-runtime-policy-evaluate.js');
34
+ const SCRIPT_AUTHORIZATION_TIER = path.resolve(__dirname, 'interactive-authorization-tier-evaluate.js');
35
+ const SCRIPT_APPROVAL = path.resolve(__dirname, 'interactive-approval-workflow.js');
36
+ const SCRIPT_ADAPTER = path.resolve(__dirname, 'interactive-moqui-adapter.js');
37
+ const SCRIPT_FEEDBACK = path.resolve(__dirname, 'interactive-feedback-log.js');
38
+ const SCRIPT_WORK_ORDER = path.resolve(__dirname, 'interactive-work-order-build.js');
39
+
40
+ function parseArgs(argv) {
41
+ const explicitKeys = new Set();
42
+ const options = {
43
+ context: null,
44
+ goal: null,
45
+ goalFile: null,
46
+ userId: DEFAULT_USER_ID,
47
+ sessionId: null,
48
+ executionMode: 'suggestion',
49
+ businessMode: null,
50
+ businessModePolicy: DEFAULT_BUSINESS_MODE_POLICY,
51
+ allowModeOverride: false,
52
+ policy: null,
53
+ catalog: null,
54
+ dialoguePolicy: null,
55
+ dialogueProfile: DEFAULT_DIALOGUE_PROFILE,
56
+ uiMode: null,
57
+ dialogueOut: null,
58
+ runtimeMode: DEFAULT_RUNTIME_MODE,
59
+ runtimeEnvironment: DEFAULT_RUNTIME_ENVIRONMENT,
60
+ runtimePolicy: null,
61
+ runtimeOut: null,
62
+ authorizationTierPolicy: null,
63
+ authorizationTierOut: null,
64
+ contextContract: null,
65
+ strictContract: true,
66
+ moquiConfig: null,
67
+ outDir: DEFAULT_OUT_DIR,
68
+ out: null,
69
+ workOrderOut: null,
70
+ workOrderMarkdownOut: null,
71
+ approvalActor: DEFAULT_APPROVAL_ACTOR,
72
+ approvalActorRole: null,
73
+ approverActor: null,
74
+ approverActorRole: null,
75
+ approvalRolePolicy: null,
76
+ skipSubmit: false,
77
+ autoApproveLowRisk: false,
78
+ autoExecuteLowRisk: false,
79
+ allowSuggestionApply: false,
80
+ liveApply: false,
81
+ dryRun: true,
82
+ feedbackScore: null,
83
+ feedbackComment: null,
84
+ feedbackTags: [],
85
+ feedbackChannel: DEFAULT_FEEDBACK_CHANNEL,
86
+ authPassword: null,
87
+ authPasswordHash: null,
88
+ authPasswordEnv: null,
89
+ failOnDialogueDeny: false,
90
+ failOnGateDeny: false,
91
+ failOnGateNonAllow: false,
92
+ failOnRuntimeNonAllow: false,
93
+ failOnExecuteBlocked: false,
94
+ json: false
95
+ };
96
+
97
+ for (let index = 0; index < argv.length; index += 1) {
98
+ const token = argv[index];
99
+ const next = argv[index + 1];
100
+
101
+ if (token === '--context' && next) {
102
+ options.context = next;
103
+ index += 1;
104
+ } else if (token === '--goal' && next) {
105
+ options.goal = next;
106
+ index += 1;
107
+ } else if (token === '--goal-file' && next) {
108
+ options.goalFile = next;
109
+ index += 1;
110
+ } else if (token === '--user-id' && next) {
111
+ options.userId = next;
112
+ index += 1;
113
+ } else if (token === '--session-id' && next) {
114
+ options.sessionId = next;
115
+ index += 1;
116
+ } else if (token === '--execution-mode' && next) {
117
+ options.executionMode = next;
118
+ explicitKeys.add('executionMode');
119
+ index += 1;
120
+ } else if (token === '--business-mode' && next) {
121
+ options.businessMode = next;
122
+ index += 1;
123
+ } else if (token === '--business-mode-policy' && next) {
124
+ options.businessModePolicy = next;
125
+ index += 1;
126
+ } else if (token === '--allow-mode-override') {
127
+ options.allowModeOverride = true;
128
+ } else if (token === '--policy' && next) {
129
+ options.policy = next;
130
+ index += 1;
131
+ } else if (token === '--catalog' && next) {
132
+ options.catalog = next;
133
+ index += 1;
134
+ } else if (token === '--dialogue-policy' && next) {
135
+ options.dialoguePolicy = next;
136
+ index += 1;
137
+ } else if (token === '--dialogue-profile' && next) {
138
+ options.dialogueProfile = next;
139
+ explicitKeys.add('dialogueProfile');
140
+ index += 1;
141
+ } else if (token === '--ui-mode' && next) {
142
+ options.uiMode = next;
143
+ explicitKeys.add('uiMode');
144
+ index += 1;
145
+ } else if (token === '--dialogue-out' && next) {
146
+ options.dialogueOut = next;
147
+ index += 1;
148
+ } else if (token === '--runtime-mode' && next) {
149
+ options.runtimeMode = next;
150
+ explicitKeys.add('runtimeMode');
151
+ index += 1;
152
+ } else if (token === '--runtime-environment' && next) {
153
+ options.runtimeEnvironment = next;
154
+ explicitKeys.add('runtimeEnvironment');
155
+ index += 1;
156
+ } else if (token === '--runtime-policy' && next) {
157
+ options.runtimePolicy = next;
158
+ index += 1;
159
+ } else if (token === '--runtime-out' && next) {
160
+ options.runtimeOut = next;
161
+ index += 1;
162
+ } else if (token === '--authorization-tier-policy' && next) {
163
+ options.authorizationTierPolicy = next;
164
+ index += 1;
165
+ } else if (token === '--authorization-tier-out' && next) {
166
+ options.authorizationTierOut = next;
167
+ index += 1;
168
+ } else if (token === '--context-contract' && next) {
169
+ options.contextContract = next;
170
+ index += 1;
171
+ } else if (token === '--no-strict-contract') {
172
+ options.strictContract = false;
173
+ } else if (token === '--moqui-config' && next) {
174
+ options.moquiConfig = next;
175
+ index += 1;
176
+ } else if (token === '--out-dir' && next) {
177
+ options.outDir = next;
178
+ index += 1;
179
+ } else if (token === '--out' && next) {
180
+ options.out = next;
181
+ index += 1;
182
+ } else if (token === '--work-order-out' && next) {
183
+ options.workOrderOut = next;
184
+ index += 1;
185
+ } else if (token === '--work-order-markdown-out' && next) {
186
+ options.workOrderMarkdownOut = next;
187
+ index += 1;
188
+ } else if (token === '--approval-actor' && next) {
189
+ options.approvalActor = next;
190
+ index += 1;
191
+ } else if (token === '--approval-actor-role' && next) {
192
+ options.approvalActorRole = next;
193
+ index += 1;
194
+ } else if (token === '--approver-actor' && next) {
195
+ options.approverActor = next;
196
+ index += 1;
197
+ } else if (token === '--approver-actor-role' && next) {
198
+ options.approverActorRole = next;
199
+ index += 1;
200
+ } else if (token === '--approval-role-policy' && next) {
201
+ options.approvalRolePolicy = next;
202
+ index += 1;
203
+ } else if (token === '--skip-submit') {
204
+ options.skipSubmit = true;
205
+ } else if (token === '--auto-approve-low-risk') {
206
+ options.autoApproveLowRisk = true;
207
+ } else if (token === '--auto-execute-low-risk') {
208
+ options.autoExecuteLowRisk = true;
209
+ explicitKeys.add('autoExecuteLowRisk');
210
+ } else if (token === '--allow-suggestion-apply') {
211
+ options.allowSuggestionApply = true;
212
+ } else if (token === '--live-apply') {
213
+ options.liveApply = true;
214
+ } else if (token === '--no-dry-run') {
215
+ options.dryRun = false;
216
+ } else if (token === '--feedback-score' && next) {
217
+ options.feedbackScore = Number(next);
218
+ index += 1;
219
+ } else if (token === '--feedback-comment' && next) {
220
+ options.feedbackComment = next;
221
+ index += 1;
222
+ } else if (token === '--feedback-tags' && next) {
223
+ options.feedbackTags = next.split(',').map(item => item.trim()).filter(Boolean);
224
+ index += 1;
225
+ } else if (token === '--feedback-channel' && next) {
226
+ options.feedbackChannel = next;
227
+ index += 1;
228
+ } else if (token === '--auth-password' && next) {
229
+ options.authPassword = next;
230
+ index += 1;
231
+ } else if (token === '--auth-password-hash' && next) {
232
+ options.authPasswordHash = next;
233
+ index += 1;
234
+ } else if (token === '--auth-password-env' && next) {
235
+ options.authPasswordEnv = next;
236
+ index += 1;
237
+ } else if (token === '--fail-on-dialogue-deny') {
238
+ options.failOnDialogueDeny = true;
239
+ } else if (token === '--fail-on-gate-deny') {
240
+ options.failOnGateDeny = true;
241
+ } else if (token === '--fail-on-gate-non-allow') {
242
+ options.failOnGateNonAllow = true;
243
+ } else if (token === '--fail-on-runtime-non-allow') {
244
+ options.failOnRuntimeNonAllow = true;
245
+ } else if (token === '--fail-on-execute-blocked') {
246
+ options.failOnExecuteBlocked = true;
247
+ } else if (token === '--json') {
248
+ options.json = true;
249
+ } else if (token === '--help' || token === '-h') {
250
+ printHelpAndExit(0);
251
+ }
252
+ }
253
+
254
+ if (!options.context) {
255
+ throw new Error('--context is required.');
256
+ }
257
+ if (!options.goal && !options.goalFile) {
258
+ throw new Error('either --goal or --goal-file is required.');
259
+ }
260
+ if (!['suggestion', 'apply'].includes(`${options.executionMode || ''}`.trim().toLowerCase())) {
261
+ throw new Error('--execution-mode must be one of: suggestion, apply');
262
+ }
263
+ if (!RUNTIME_MODES.has(`${options.runtimeMode || ''}`.trim().toLowerCase())) {
264
+ throw new Error(`--runtime-mode must be one of: ${Array.from(RUNTIME_MODES).join(', ')}`);
265
+ }
266
+ if (!RUNTIME_ENVIRONMENTS.has(`${options.runtimeEnvironment || ''}`.trim().toLowerCase())) {
267
+ throw new Error(`--runtime-environment must be one of: ${Array.from(RUNTIME_ENVIRONMENTS).join(', ')}`);
268
+ }
269
+ if (options.feedbackScore != null) {
270
+ if (!Number.isFinite(options.feedbackScore) || options.feedbackScore < 0 || options.feedbackScore > 5) {
271
+ throw new Error('--feedback-score must be between 0 and 5.');
272
+ }
273
+ }
274
+ if (options.authPasswordHash != null) {
275
+ const normalizedHash = `${options.authPasswordHash || ''}`.trim();
276
+ if (!/^[a-fA-F0-9]{64}$/.test(normalizedHash)) {
277
+ throw new Error('--auth-password-hash must be a sha256 hex string (64 chars).');
278
+ }
279
+ }
280
+ if (options.authPasswordEnv != null && `${options.authPasswordEnv || ''}`.trim().length === 0) {
281
+ throw new Error('--auth-password-env cannot be empty.');
282
+ }
283
+
284
+ options.userId = `${options.userId || ''}`.trim() || DEFAULT_USER_ID;
285
+ options.approvalActor = `${options.approvalActor || ''}`.trim() || DEFAULT_APPROVAL_ACTOR;
286
+ options.approvalActorRole = `${options.approvalActorRole || ''}`.trim().toLowerCase() || null;
287
+ options.approverActor = `${options.approverActor || ''}`.trim() || options.approvalActor;
288
+ options.approverActorRole = `${options.approverActorRole || ''}`.trim().toLowerCase() || options.approvalActorRole;
289
+ options.approvalRolePolicy = `${options.approvalRolePolicy || ''}`.trim() || null;
290
+ options.feedbackComment = `${options.feedbackComment || ''}`.trim() || null;
291
+ options.dialoguePolicy = `${options.dialoguePolicy || ''}`.trim() || null;
292
+ options.executionMode = `${options.executionMode || ''}`.trim().toLowerCase() || 'suggestion';
293
+ options.businessMode = normalizeBusinessMode(options.businessMode);
294
+ options.businessModePolicy = `${options.businessModePolicy || ''}`.trim() || DEFAULT_BUSINESS_MODE_POLICY;
295
+ options.dialogueProfile = `${options.dialogueProfile || ''}`.trim().toLowerCase() || DEFAULT_DIALOGUE_PROFILE;
296
+ options.uiMode = `${options.uiMode || ''}`.trim().toLowerCase()
297
+ || (options.dialogueProfile === 'system-maintainer' ? 'ops-console' : 'user-app');
298
+ options.dialogueOut = `${options.dialogueOut || ''}`.trim() || null;
299
+ options.runtimeMode = `${options.runtimeMode || ''}`.trim().toLowerCase() || DEFAULT_RUNTIME_MODE;
300
+ options.runtimeEnvironment = `${options.runtimeEnvironment || ''}`.trim().toLowerCase() || DEFAULT_RUNTIME_ENVIRONMENT;
301
+ options.runtimePolicy = `${options.runtimePolicy || ''}`.trim() || null;
302
+ options.runtimeOut = `${options.runtimeOut || ''}`.trim() || null;
303
+ options.authorizationTierPolicy = `${options.authorizationTierPolicy || ''}`.trim() || null;
304
+ options.authorizationTierOut = `${options.authorizationTierOut || ''}`.trim() || null;
305
+ options.feedbackChannel = `${options.feedbackChannel || ''}`.trim().toLowerCase() || DEFAULT_FEEDBACK_CHANNEL;
306
+ options.authPassword = options.authPassword == null ? null : `${options.authPassword}`;
307
+ options.authPasswordHash = options.authPasswordHash == null
308
+ ? null
309
+ : `${options.authPasswordHash}`.trim().toLowerCase();
310
+ options.authPasswordEnv = `${options.authPasswordEnv || ''}`.trim() || null;
311
+ options.workOrderOut = `${options.workOrderOut || ''}`.trim() || null;
312
+ options.workOrderMarkdownOut = `${options.workOrderMarkdownOut || ''}`.trim() || null;
313
+ if (!FEEDBACK_CHANNELS.has(options.feedbackChannel)) {
314
+ throw new Error(`--feedback-channel must be one of: ${Array.from(FEEDBACK_CHANNELS).join(', ')}`);
315
+ }
316
+ options.feedbackTags = Array.from(new Set(options.feedbackTags.map(item => item.toLowerCase())));
317
+ if (!DIALOGUE_PROFILES.has(options.dialogueProfile)) {
318
+ throw new Error(`--dialogue-profile must be one of: ${Array.from(DIALOGUE_PROFILES).join(', ')}`);
319
+ }
320
+ if (!UI_MODES.has(options.uiMode)) {
321
+ throw new Error(`--ui-mode must be one of: ${Array.from(UI_MODES).join(', ')}`);
322
+ }
323
+ if (options.businessMode && !BUSINESS_MODES.has(options.businessMode)) {
324
+ throw new Error(`--business-mode must be one of: ${Array.from(BUSINESS_MODES).join(', ')}`);
325
+ }
326
+ options.businessModeState = applyBusinessModePolicy(options, explicitKeys, process.cwd());
327
+ if (!['suggestion', 'apply'].includes(options.executionMode)) {
328
+ throw new Error('--execution-mode must be one of: suggestion, apply');
329
+ }
330
+ if (!DIALOGUE_PROFILES.has(options.dialogueProfile)) {
331
+ throw new Error(`--dialogue-profile must be one of: ${Array.from(DIALOGUE_PROFILES).join(', ')}`);
332
+ }
333
+ if (!UI_MODES.has(options.uiMode)) {
334
+ throw new Error(`--ui-mode must be one of: ${Array.from(UI_MODES).join(', ')}`);
335
+ }
336
+ if (!RUNTIME_MODES.has(options.runtimeMode)) {
337
+ throw new Error(`--runtime-mode must be one of: ${Array.from(RUNTIME_MODES).join(', ')}`);
338
+ }
339
+ if (!RUNTIME_ENVIRONMENTS.has(options.runtimeEnvironment)) {
340
+ throw new Error(`--runtime-environment must be one of: ${Array.from(RUNTIME_ENVIRONMENTS).join(', ')}`);
341
+ }
342
+
343
+ return options;
344
+ }
345
+
346
+ function printHelpAndExit(code) {
347
+ const lines = [
348
+ 'Usage: node scripts/interactive-customization-loop.js --context <path> (--goal <text> | --goal-file <path>) [options]',
349
+ '',
350
+ 'Pipeline:',
351
+ ' dialogue -> intent -> plan -> gate -> runtime -> authorization-tier -> approval(init/submit) -> optional low-risk apply',
352
+ '',
353
+ 'Options:',
354
+ ' --context <path> Page context JSON file (required)',
355
+ ' --goal <text> Business goal text',
356
+ ' --goal-file <path> File containing business goal text',
357
+ ` --user-id <id> User identifier (default: ${DEFAULT_USER_ID})`,
358
+ ' --session-id <id> Session id (default: auto-generated)',
359
+ ' --execution-mode <mode> suggestion|apply (default: suggestion)',
360
+ ' --business-mode <mode> user-mode|ops-mode|dev-mode (preset routing profile)',
361
+ ` --business-mode-policy <path> Business mode policy override (default: ${DEFAULT_BUSINESS_MODE_POLICY})`,
362
+ ' --allow-mode-override Allow option overrides that conflict with business mode preset',
363
+ ' --policy <path> Guardrail policy override',
364
+ ' --catalog <path> High-risk catalog override',
365
+ ' --dialogue-policy <path> Dialogue governance policy override',
366
+ ` --dialogue-profile <name> business-user|system-maintainer (default: ${DEFAULT_DIALOGUE_PROFILE})`,
367
+ ` --ui-mode <name> user-app|ops-console (default by dialogue profile)`,
368
+ ' --dialogue-out <path> Dialogue governance report output path',
369
+ ` --runtime-mode <name> user-assist|ops-fix|feature-dev (default: ${DEFAULT_RUNTIME_MODE})`,
370
+ ` --runtime-environment <name> dev|staging|prod (default: ${DEFAULT_RUNTIME_ENVIRONMENT})`,
371
+ ' --runtime-policy <path> Runtime mode/environment policy override',
372
+ ' --runtime-out <path> Runtime policy evaluation report output path',
373
+ ' --authorization-tier-policy <path> Authorization tier policy override',
374
+ ' --authorization-tier-out <path> Authorization tier evaluation report output path',
375
+ ' --context-contract <path> Context contract override for intent build',
376
+ ' --no-strict-contract Do not fail when context contract validation has issues',
377
+ ' --moqui-config <path> Moqui adapter runtime config',
378
+ ` --out-dir <path> Loop artifact root (default: ${DEFAULT_OUT_DIR})`,
379
+ ' --out <path> Loop summary JSON output path',
380
+ ' --work-order-out <path> Work-order JSON output path',
381
+ ' --work-order-markdown-out <path> Work-order markdown output path',
382
+ ` --approval-actor <id> Approval workflow actor (default: ${DEFAULT_APPROVAL_ACTOR})`,
383
+ ' --approval-actor-role <name> Approval actor role for role-policy checks',
384
+ ' --approver-actor <id> Auto-approve actor (default: approval actor)',
385
+ ' --approver-actor-role <name> Auto-approve actor role (default: approval actor role)',
386
+ ' --approval-role-policy <path> Approval role policy JSON path',
387
+ ' --skip-submit Skip approval submit step',
388
+ ' --auto-approve-low-risk Auto-approve when gate=allow and risk=low',
389
+ ' --auto-execute-low-risk Auto-run adapter low-risk-apply when gate=allow and risk=low',
390
+ ' --allow-suggestion-apply Allow applying plans with execution_mode=suggestion',
391
+ ' --live-apply Enable live apply mode for adapter',
392
+ ' --no-dry-run Disable dry-run simulation when live apply is enabled',
393
+ ' --feedback-score <0..5> Optional user feedback score to append into feedback JSONL',
394
+ ' --feedback-comment <text> Optional user feedback comment',
395
+ ' --feedback-tags <csv> Optional feedback tags (comma-separated)',
396
+ ` --feedback-channel <name> ui|cli|api|other (default: ${DEFAULT_FEEDBACK_CHANNEL})`,
397
+ ' --auth-password <text> One-time password for protected execute action',
398
+ ' --auth-password-hash <sha256> Password verifier hash override',
399
+ ` --auth-password-env <name> Password hash env name (default: ${DEFAULT_AUTH_PASSWORD_HASH_ENV})`,
400
+ ' --fail-on-dialogue-deny Exit code 2 if dialogue decision is deny',
401
+ ' --fail-on-gate-deny Exit code 2 if gate decision is deny',
402
+ ' --fail-on-gate-non-allow Exit code 2 if gate decision is deny/review-required',
403
+ ' --fail-on-runtime-non-allow Exit code 2 if runtime decision is deny/review-required',
404
+ ' --fail-on-execute-blocked Exit code 2 if auto execute result is blocked/non-success',
405
+ ' --json Print loop summary JSON',
406
+ ' -h, --help Show this help'
407
+ ];
408
+ console.log(lines.join('\n'));
409
+ process.exit(code);
410
+ }
411
+
412
+ function resolvePath(cwd, value) {
413
+ return path.isAbsolute(value) ? value : path.resolve(cwd, value);
414
+ }
415
+
416
+ function normalizeSessionId(value) {
417
+ const raw = `${value || ''}`.trim();
418
+ if (raw.length > 0) {
419
+ return raw.replace(/[^\w.-]/g, '-');
420
+ }
421
+ return `session-${crypto.randomUUID()}`;
422
+ }
423
+
424
+ function parseJsonOutput(text, label) {
425
+ const raw = `${text || ''}`.trim();
426
+ if (!raw) {
427
+ throw new Error(`${label} produced empty stdout`);
428
+ }
429
+ try {
430
+ return JSON.parse(raw);
431
+ } catch (error) {
432
+ throw new Error(`${label} returned invalid JSON: ${error.message}`);
433
+ }
434
+ }
435
+
436
+ function buildCommandString(scriptPath, args, redactFlags = []) {
437
+ const redacted = new Set(redactFlags);
438
+ const maskedArgs = [];
439
+ for (let index = 0; index < args.length; index += 1) {
440
+ const token = args[index];
441
+ maskedArgs.push(token);
442
+ if (redacted.has(token)) {
443
+ if (index + 1 < args.length) {
444
+ maskedArgs.push('***');
445
+ index += 1;
446
+ }
447
+ }
448
+ }
449
+ return [process.execPath, scriptPath, ...maskedArgs].join(' ');
450
+ }
451
+
452
+ function runScript({
453
+ label,
454
+ scriptPath,
455
+ args,
456
+ cwd,
457
+ allowedExitCodes
458
+ }) {
459
+ const result = spawnSync(process.execPath, [scriptPath, ...args], {
460
+ cwd,
461
+ encoding: 'utf8'
462
+ });
463
+ const exitCode = typeof result.status === 'number' ? result.status : 1;
464
+
465
+ if (!allowedExitCodes.includes(exitCode)) {
466
+ const stderr = `${result.stderr || ''}`.trim();
467
+ throw new Error(
468
+ `${label} failed with exit code ${exitCode}${stderr ? `: ${stderr}` : ''}`
469
+ );
470
+ }
471
+
472
+ return {
473
+ exit_code: exitCode,
474
+ stdout: `${result.stdout || ''}`,
475
+ stderr: `${result.stderr || ''}`
476
+ };
477
+ }
478
+
479
+ function toRelative(cwd, absolutePath) {
480
+ return path.relative(cwd, absolutePath) || '.';
481
+ }
482
+
483
+ function buildStep(name, payload, command, exitCode) {
484
+ return {
485
+ name,
486
+ command,
487
+ exit_code: exitCode,
488
+ payload
489
+ };
490
+ }
491
+
492
+ function shouldAutoLowRisk({
493
+ gateDecision,
494
+ riskLevel,
495
+ dialogueDecision,
496
+ runtimeDecision,
497
+ runtimeAutoExecuteAllowed,
498
+ authorizationTierDecision,
499
+ authorizationTierAutoExecuteAllowed
500
+ }) {
501
+ return `${dialogueDecision || ''}`.trim().toLowerCase() !== 'deny' &&
502
+ `${gateDecision || ''}`.trim().toLowerCase() === 'allow' &&
503
+ `${riskLevel || ''}`.trim().toLowerCase() === 'low' &&
504
+ `${runtimeDecision || ''}`.trim().toLowerCase() === 'allow' &&
505
+ runtimeAutoExecuteAllowed === true &&
506
+ `${authorizationTierDecision || ''}`.trim().toLowerCase() === 'allow' &&
507
+ authorizationTierAutoExecuteAllowed === true;
508
+ }
509
+
510
+ function buildSummaryStatus({
511
+ dialogueDecision,
512
+ gateDecision,
513
+ runtimeDecision,
514
+ authorizationTierDecision,
515
+ executionAttempted,
516
+ executionBlocked,
517
+ executionResult
518
+ }) {
519
+ const normalizedDialogueDecision = `${dialogueDecision || ''}`.trim().toLowerCase();
520
+ const decision = `${gateDecision || ''}`.trim().toLowerCase();
521
+ const runtime = `${runtimeDecision || ''}`.trim().toLowerCase();
522
+ const authorization = `${authorizationTierDecision || ''}`.trim().toLowerCase();
523
+ if (normalizedDialogueDecision === 'deny') {
524
+ return 'blocked';
525
+ }
526
+ if (authorization === 'deny') {
527
+ return 'blocked';
528
+ }
529
+ if (authorization === 'review-required') {
530
+ return 'requires-review';
531
+ }
532
+ if (runtime === 'deny') {
533
+ return 'blocked';
534
+ }
535
+ if (runtime === 'review-required') {
536
+ return 'requires-review';
537
+ }
538
+ if (normalizedDialogueDecision === 'clarify' && decision === 'allow' && !executionAttempted) {
539
+ return 'requires-clarification';
540
+ }
541
+ if (decision === 'deny') {
542
+ return 'blocked';
543
+ }
544
+ if (decision === 'review-required') {
545
+ return 'requires-review';
546
+ }
547
+ if (!executionAttempted) {
548
+ return 'ready-for-apply';
549
+ }
550
+ if (executionBlocked) {
551
+ return 'apply-blocked';
552
+ }
553
+ if (`${executionResult || ''}`.trim().toLowerCase() === 'success') {
554
+ return 'completed';
555
+ }
556
+ return 'apply-finished-with-risk';
557
+ }
558
+
559
+ function detectExecutionBlockReasonCategory(execution) {
560
+ if (!execution || execution.blocked !== true) {
561
+ return null;
562
+ }
563
+ const reason = `${execution.reason || ''}`.trim().toLowerCase();
564
+ const decision = `${execution.decision || ''}`.trim().toLowerCase();
565
+
566
+ if (reason.includes('password authorization')) {
567
+ return 'password-authorization';
568
+ }
569
+ if (reason.includes('authorization tier')) {
570
+ return 'authorization-tier';
571
+ }
572
+ if (reason.includes('actor role') || reason.includes('allowed roles')) {
573
+ return 'role-policy';
574
+ }
575
+ if (decision === 'runtime-blocked' || reason.includes('runtime environment')) {
576
+ return 'runtime-policy';
577
+ }
578
+ if (decision === 'approval-blocked') {
579
+ return 'approval-policy';
580
+ }
581
+ return 'unknown';
582
+ }
583
+
584
+ function buildExecutionBlockRemediationHint({
585
+ blockReasonCategory,
586
+ executionReason
587
+ }) {
588
+ if (!blockReasonCategory) {
589
+ return null;
590
+ }
591
+ if (blockReasonCategory === 'password-authorization') {
592
+ return 'Provide one-time password authorization and rerun apply.';
593
+ }
594
+ if (blockReasonCategory === 'role-policy') {
595
+ return 'Use an actor role allowed by approval role policy for this action and rerun apply.';
596
+ }
597
+ if (blockReasonCategory === 'authorization-tier') {
598
+ return 'Switch to an authorized dialogue profile/environment and satisfy secondary authorization requirements.';
599
+ }
600
+ if (blockReasonCategory === 'runtime-policy') {
601
+ return 'Adjust runtime mode/environment policy or run under an environment that permits the action.';
602
+ }
603
+ if (blockReasonCategory === 'approval-policy') {
604
+ return 'Complete required approval workflow transitions before execute.';
605
+ }
606
+ if (`${executionReason || ''}`.trim().length > 0) {
607
+ return `Review execution block reason: ${executionReason}`;
608
+ }
609
+ return 'Review execution block reason and approval/runtime policies.';
610
+ }
611
+
612
+ function buildNextActions({
613
+ dialogueDecision,
614
+ gateDecision,
615
+ runtimeDecision,
616
+ authorizationTierDecision,
617
+ riskLevel,
618
+ runtime,
619
+ authorizationTier,
620
+ autoExecuteTriggered,
621
+ executionPayload,
622
+ executionBlockReasonCategory,
623
+ executionReason,
624
+ approvalStatus,
625
+ feedbackLogged,
626
+ artifacts
627
+ }) {
628
+ const actions = [];
629
+ const dialogue = `${dialogueDecision || ''}`.trim().toLowerCase();
630
+ const decision = `${gateDecision || ''}`.trim().toLowerCase();
631
+ const runtimeDecisionNormalized = `${runtimeDecision || ''}`.trim().toLowerCase();
632
+ const authorizationDecision = `${authorizationTierDecision || ''}`.trim().toLowerCase();
633
+ const risk = `${riskLevel || ''}`.trim().toLowerCase();
634
+
635
+ if (dialogue === 'deny') {
636
+ actions.push('Rewrite the goal to remove sensitive/forbidden requests and rerun dialogue governance.');
637
+ actions.push(`node scripts/interactive-dialogue-governance.js --goal "..." --context ${artifacts.context_json} --json`);
638
+ } else if (authorizationDecision === 'deny') {
639
+ actions.push('Authorization tier denied apply for current profile/environment.');
640
+ actions.push(`node scripts/interactive-authorization-tier-evaluate.js --execution-mode apply --dialogue-profile ${authorizationTier.profile || DEFAULT_DIALOGUE_PROFILE} --runtime-environment ${runtime.environment || DEFAULT_RUNTIME_ENVIRONMENT} --json`);
641
+ actions.push('Use suggestion mode for business-user, or switch to system-maintainer with explicit approval controls.');
642
+ } else if (authorizationDecision === 'review-required') {
643
+ actions.push('Authorization tier requires manual review before apply.');
644
+ actions.push('Complete secondary authorization checks (password/role separation/change ticket) before execution.');
645
+ } else if (runtimeDecisionNormalized === 'deny') {
646
+ actions.push('Runtime policy denied the plan. Switch runtime mode/environment or reduce risky actions.');
647
+ actions.push(`node scripts/interactive-runtime-policy-evaluate.js --plan ${artifacts.plan_json} --ui-mode ${runtime.ui_mode || 'user-app'} --runtime-mode ${runtime.mode || DEFAULT_RUNTIME_MODE} --runtime-environment ${runtime.environment || DEFAULT_RUNTIME_ENVIRONMENT} --json`);
648
+ } else if (runtimeDecisionNormalized === 'review-required') {
649
+ actions.push('Runtime policy requires manual review before apply.');
650
+ actions.push('Complete review ticket and approval workflow before any execution step.');
651
+ } else if (decision === 'deny') {
652
+ actions.push('Revise business goal wording and regenerate intent/plan before retrying.');
653
+ actions.push('Review gate failure reasons in the generated gate markdown report.');
654
+ } else if (decision === 'review-required') {
655
+ actions.push('Complete manual approval review before any apply step.');
656
+ actions.push('Tune plan actions to reduce sensitive or high-risk operations.');
657
+ } else if (!autoExecuteTriggered) {
658
+ actions.push('Review plan and gate outputs, then run adapter low-risk apply when ready.');
659
+ actions.push(`node scripts/interactive-moqui-adapter.js --action low-risk-apply --plan ${artifacts.plan_json} --json`);
660
+ } else if (executionPayload && executionPayload.execution_record) {
661
+ const record = executionPayload.execution_record;
662
+ if (`${record.result || ''}`.trim().toLowerCase() === 'success') {
663
+ if (feedbackLogged) {
664
+ actions.push(`Execution succeeded (execution_id=${record.execution_id || 'n/a'}) and feedback was logged.`);
665
+ actions.push('node scripts/interactive-governance-report.js --period weekly --json');
666
+ } else {
667
+ actions.push(`Execution succeeded (execution_id=${record.execution_id || 'n/a'}). Collect user feedback for governance.`);
668
+ actions.push('node scripts/interactive-feedback-log.js --score 5 --comment "business flow improved" --json');
669
+ }
670
+ } else {
671
+ actions.push('Execution was blocked or failed. Inspect adapter output and adjust plan/policy.');
672
+ actions.push(`node scripts/interactive-moqui-adapter.js --action validate --plan ${artifacts.plan_json} --json`);
673
+ }
674
+ } else if (executionBlockReasonCategory === 'password-authorization') {
675
+ actions.push('Provide one-time password authorization and rerun low-risk apply.');
676
+ actions.push(`node scripts/interactive-customization-loop.js --context ${artifacts.context_json} --goal "..." --execution-mode apply --auto-execute-low-risk --auth-password "<password>" --json`);
677
+ } else if (executionBlockReasonCategory === 'authorization-tier') {
678
+ actions.push('Satisfy authorization tier step-up requirements (password/role policy/role separation) and rerun apply.');
679
+ actions.push(`node scripts/interactive-customization-loop.js --context ${artifacts.context_json} --goal "..." --dialogue-profile system-maintainer --execution-mode apply --auto-execute-low-risk --auth-password "<password>" --json`);
680
+ } else if (executionBlockReasonCategory === 'role-policy') {
681
+ actions.push('Use an approver actor role allowed by approval role policy, then rerun low-risk apply.');
682
+ actions.push(`node scripts/interactive-approval-workflow.js --action status --state-file ${artifacts.approval_state_json} --json`);
683
+ actions.push(`node scripts/interactive-customization-loop.js --context ${artifacts.context_json} --goal "..." --execution-mode apply --auto-execute-low-risk --approval-role-policy "<role-policy.json>" --approver-actor-role "<allowed-role>" --json`);
684
+ }
685
+
686
+ if (risk === 'low' && approvalStatus === 'submitted') {
687
+ actions.push('Optional: approve workflow to keep an auditable close-loop trail.');
688
+ }
689
+ return actions;
690
+ }
691
+
692
+ async function main() {
693
+ const options = parseArgs(process.argv.slice(2));
694
+ const cwd = process.cwd();
695
+ const sessionId = normalizeSessionId(options.sessionId);
696
+
697
+ const contextPath = resolvePath(cwd, options.context);
698
+ const globalFeedbackPath = resolvePath(cwd, '.sce/reports/interactive-user-feedback.jsonl');
699
+ const globalDialogueAuthorizationSignalsPath = resolvePath(cwd, '.sce/reports/interactive-dialogue-authorization-signals.jsonl');
700
+ const globalRuntimeSignalsPath = resolvePath(cwd, '.sce/reports/interactive-runtime-signals.jsonl');
701
+ const globalAuthorizationTierSignalsPath = resolvePath(cwd, '.sce/reports/interactive-authorization-tier-signals.jsonl');
702
+ const outRoot = resolvePath(cwd, options.outDir);
703
+ const sessionDir = path.join(outRoot, sessionId);
704
+ const summaryOutPath = options.out
705
+ ? resolvePath(cwd, options.out)
706
+ : path.join(sessionDir, 'interactive-customization-loop.summary.json');
707
+
708
+ const artifacts = {
709
+ session_dir: toRelative(cwd, sessionDir),
710
+ context_json: toRelative(cwd, contextPath),
711
+ dialogue_json: toRelative(cwd, options.dialogueOut
712
+ ? resolvePath(cwd, options.dialogueOut)
713
+ : path.join(sessionDir, 'interactive-dialogue-governance.json')),
714
+ runtime_json: toRelative(cwd, options.runtimeOut
715
+ ? resolvePath(cwd, options.runtimeOut)
716
+ : path.join(sessionDir, 'interactive-runtime-policy.json')),
717
+ authorization_tier_json: toRelative(cwd, options.authorizationTierOut
718
+ ? resolvePath(cwd, options.authorizationTierOut)
719
+ : path.join(sessionDir, 'interactive-authorization-tier.json')),
720
+ intent_json: toRelative(cwd, path.join(sessionDir, 'interactive-change-intent.json')),
721
+ explain_md: toRelative(cwd, path.join(sessionDir, 'interactive-page-explain.md')),
722
+ intent_audit_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-copilot-audit.jsonl')),
723
+ plan_json: toRelative(cwd, path.join(sessionDir, 'interactive-change-plan.generated.json')),
724
+ plan_md: toRelative(cwd, path.join(sessionDir, 'interactive-change-plan.generated.md')),
725
+ gate_json: toRelative(cwd, path.join(sessionDir, 'interactive-change-plan-gate.json')),
726
+ gate_md: toRelative(cwd, path.join(sessionDir, 'interactive-change-plan-gate.md')),
727
+ approval_state_json: toRelative(cwd, path.join(sessionDir, 'interactive-approval-state.json')),
728
+ approval_audit_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-approval-events.jsonl')),
729
+ adapter_json: toRelative(cwd, path.join(sessionDir, 'interactive-moqui-adapter.json')),
730
+ work_order_json: toRelative(cwd, options.workOrderOut
731
+ ? resolvePath(cwd, options.workOrderOut)
732
+ : path.join(sessionDir, 'interactive-work-order.json')),
733
+ work_order_md: toRelative(cwd, options.workOrderMarkdownOut
734
+ ? resolvePath(cwd, options.workOrderMarkdownOut)
735
+ : path.join(sessionDir, 'interactive-work-order.md')),
736
+ dialogue_authorization_signals_global_jsonl: toRelative(cwd, globalDialogueAuthorizationSignalsPath),
737
+ dialogue_authorization_signals_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-dialogue-authorization-signals.jsonl')),
738
+ runtime_signals_global_jsonl: toRelative(cwd, globalRuntimeSignalsPath),
739
+ runtime_signals_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-runtime-signals.jsonl')),
740
+ authorization_tier_signals_global_jsonl: toRelative(cwd, globalAuthorizationTierSignalsPath),
741
+ authorization_tier_signals_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-authorization-tier-signals.jsonl')),
742
+ feedback_global_jsonl: toRelative(cwd, globalFeedbackPath),
743
+ feedback_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-user-feedback.jsonl')),
744
+ summary_json: toRelative(cwd, summaryOutPath)
745
+ };
746
+
747
+ await fs.ensureDir(sessionDir);
748
+
749
+ const steps = [];
750
+
751
+ const dialogueArgs = [
752
+ '--context', contextPath,
753
+ '--ui-mode', options.uiMode,
754
+ '--execution-mode', options.executionMode,
755
+ '--runtime-environment', options.runtimeEnvironment,
756
+ '--out', resolvePath(cwd, artifacts.dialogue_json),
757
+ '--json'
758
+ ];
759
+ if (options.goal) {
760
+ dialogueArgs.push('--goal', options.goal);
761
+ } else {
762
+ dialogueArgs.push('--goal-file', resolvePath(cwd, options.goalFile));
763
+ }
764
+ if (options.dialoguePolicy) {
765
+ dialogueArgs.push('--policy', resolvePath(cwd, options.dialoguePolicy));
766
+ }
767
+ if (options.dialogueProfile) {
768
+ dialogueArgs.push('--profile', options.dialogueProfile);
769
+ }
770
+ const dialogueResult = runScript({
771
+ label: 'interactive-dialogue-governance',
772
+ scriptPath: SCRIPT_DIALOGUE,
773
+ args: dialogueArgs,
774
+ cwd,
775
+ allowedExitCodes: [0]
776
+ });
777
+ const dialoguePayload = parseJsonOutput(dialogueResult.stdout, 'interactive-dialogue-governance');
778
+ const dialogueDecision = dialoguePayload && dialoguePayload.decision ? dialoguePayload.decision : 'allow';
779
+ const dialogueAuthorization = dialoguePayload &&
780
+ dialoguePayload.authorization_dialogue &&
781
+ typeof dialoguePayload.authorization_dialogue === 'object'
782
+ ? dialoguePayload.authorization_dialogue
783
+ : {};
784
+ const dialogueAuthorizationDecision = `${dialogueAuthorization.decision || 'allow'}`.trim().toLowerCase() || 'allow';
785
+ const dialogueAuthorizationSignalRecord = {
786
+ event_type: 'interactive.dialogue.authorization.evaluated',
787
+ timestamp: new Date().toISOString(),
788
+ session_id: sessionId,
789
+ user_id: options.userId,
790
+ business_mode: options.businessMode,
791
+ ui_mode: options.uiMode,
792
+ dialogue_profile: options.dialogueProfile,
793
+ execution_mode: options.executionMode,
794
+ runtime_environment: options.runtimeEnvironment,
795
+ decision: dialogueAuthorizationDecision,
796
+ reasons: Array.isArray(dialogueAuthorization.reasons) ? dialogueAuthorization.reasons : [],
797
+ required_inputs: Array.isArray(dialogueAuthorization.required_inputs) ? dialogueAuthorization.required_inputs : [],
798
+ required_confirmation_steps: Array.isArray(dialogueAuthorization.required_confirmation_steps)
799
+ ? dialogueAuthorization.required_confirmation_steps
800
+ : []
801
+ };
802
+ const dialogueAuthorizationSignalsGlobalPath = resolvePath(cwd, artifacts.dialogue_authorization_signals_global_jsonl);
803
+ await fs.ensureDir(path.dirname(dialogueAuthorizationSignalsGlobalPath));
804
+ await fs.appendFile(dialogueAuthorizationSignalsGlobalPath, `${JSON.stringify(dialogueAuthorizationSignalRecord)}\n`, 'utf8');
805
+ const dialogueAuthorizationSignalsSessionPath = resolvePath(cwd, artifacts.dialogue_authorization_signals_jsonl);
806
+ await fs.ensureDir(path.dirname(dialogueAuthorizationSignalsSessionPath));
807
+ await fs.appendFile(dialogueAuthorizationSignalsSessionPath, `${JSON.stringify(dialogueAuthorizationSignalRecord)}\n`, 'utf8');
808
+ steps.push(buildStep(
809
+ 'dialogue',
810
+ dialoguePayload,
811
+ buildCommandString(SCRIPT_DIALOGUE, dialogueArgs),
812
+ dialogueResult.exit_code
813
+ ));
814
+
815
+ const intentArgs = [
816
+ '--context', contextPath,
817
+ '--user-id', options.userId,
818
+ '--session-id', sessionId,
819
+ '--out-intent', resolvePath(cwd, artifacts.intent_json),
820
+ '--out-explain', resolvePath(cwd, artifacts.explain_md),
821
+ '--audit-file', resolvePath(cwd, artifacts.intent_audit_jsonl),
822
+ '--json'
823
+ ];
824
+ if (options.goal) {
825
+ intentArgs.push('--goal', options.goal);
826
+ } else {
827
+ intentArgs.push('--goal-file', resolvePath(cwd, options.goalFile));
828
+ }
829
+ if (options.contextContract) {
830
+ intentArgs.push('--context-contract', resolvePath(cwd, options.contextContract));
831
+ }
832
+ if (!options.strictContract) {
833
+ intentArgs.push('--no-strict-contract');
834
+ }
835
+ const intentResult = runScript({
836
+ label: 'interactive-intent-build',
837
+ scriptPath: SCRIPT_INTENT,
838
+ args: intentArgs,
839
+ cwd,
840
+ allowedExitCodes: [0]
841
+ });
842
+ const intentPayload = parseJsonOutput(intentResult.stdout, 'interactive-intent-build');
843
+ steps.push(buildStep('intent', intentPayload, buildCommandString(SCRIPT_INTENT, intentArgs), intentResult.exit_code));
844
+
845
+ const planArgs = [
846
+ '--intent', resolvePath(cwd, artifacts.intent_json),
847
+ '--context', contextPath,
848
+ '--execution-mode', options.executionMode,
849
+ '--out-plan', resolvePath(cwd, artifacts.plan_json),
850
+ '--out-markdown', resolvePath(cwd, artifacts.plan_md),
851
+ '--json'
852
+ ];
853
+ const planResult = runScript({
854
+ label: 'interactive-plan-build',
855
+ scriptPath: SCRIPT_PLAN,
856
+ args: planArgs,
857
+ cwd,
858
+ allowedExitCodes: [0]
859
+ });
860
+ const planPayload = parseJsonOutput(planResult.stdout, 'interactive-plan-build');
861
+ steps.push(buildStep('plan', planPayload, buildCommandString(SCRIPT_PLAN, planArgs), planResult.exit_code));
862
+
863
+ const gateArgs = [
864
+ '--plan', resolvePath(cwd, artifacts.plan_json),
865
+ '--out', resolvePath(cwd, artifacts.gate_json),
866
+ '--markdown-out', resolvePath(cwd, artifacts.gate_md),
867
+ '--json'
868
+ ];
869
+ if (options.policy) {
870
+ gateArgs.push('--policy', resolvePath(cwd, options.policy));
871
+ }
872
+ if (options.catalog) {
873
+ gateArgs.push('--catalog', resolvePath(cwd, options.catalog));
874
+ }
875
+ const gateResult = runScript({
876
+ label: 'interactive-change-plan-gate',
877
+ scriptPath: SCRIPT_GATE,
878
+ args: gateArgs,
879
+ cwd,
880
+ allowedExitCodes: [0]
881
+ });
882
+ const gatePayload = parseJsonOutput(gateResult.stdout, 'interactive-change-plan-gate');
883
+ steps.push(buildStep('gate', gatePayload, buildCommandString(SCRIPT_GATE, gateArgs), gateResult.exit_code));
884
+
885
+ const gateDecision = gatePayload && gatePayload.decision ? gatePayload.decision : 'deny';
886
+ const riskLevel = planPayload && planPayload.plan ? planPayload.plan.risk_level : null;
887
+
888
+ const runtimeArgs = [
889
+ '--plan', resolvePath(cwd, artifacts.plan_json),
890
+ '--ui-mode', options.uiMode,
891
+ '--runtime-mode', options.runtimeMode,
892
+ '--runtime-environment', options.runtimeEnvironment,
893
+ '--out', resolvePath(cwd, artifacts.runtime_json),
894
+ '--json'
895
+ ];
896
+ if (options.runtimePolicy) {
897
+ runtimeArgs.push('--policy', resolvePath(cwd, options.runtimePolicy));
898
+ }
899
+ const runtimeResult = runScript({
900
+ label: 'interactive-runtime-policy-evaluate',
901
+ scriptPath: SCRIPT_RUNTIME,
902
+ args: runtimeArgs,
903
+ cwd,
904
+ allowedExitCodes: [0]
905
+ });
906
+ const runtimePayload = parseJsonOutput(runtimeResult.stdout, 'interactive-runtime-policy-evaluate');
907
+ steps.push(buildStep(
908
+ 'runtime',
909
+ runtimePayload,
910
+ buildCommandString(SCRIPT_RUNTIME, runtimeArgs),
911
+ runtimeResult.exit_code
912
+ ));
913
+ const runtimeDecision = runtimePayload && runtimePayload.decision ? runtimePayload.decision : 'deny';
914
+ const runtimeRequirements = runtimePayload && runtimePayload.requirements && typeof runtimePayload.requirements === 'object'
915
+ ? runtimePayload.requirements
916
+ : {};
917
+ const runtimeViolationCodes = Array.isArray(runtimePayload && runtimePayload.violations)
918
+ ? runtimePayload.violations
919
+ .map(item => `${item && item.code ? item.code : ''}`.trim().toLowerCase())
920
+ .filter(Boolean)
921
+ : [];
922
+ const runtimeUiModeViolationCodes = runtimeViolationCodes
923
+ .filter(code => code.startsWith('ui-mode-'));
924
+ const runtimeSignalRecord = {
925
+ event_type: 'interactive.runtime.policy.evaluated',
926
+ timestamp: new Date().toISOString(),
927
+ session_id: sessionId,
928
+ user_id: options.userId,
929
+ business_mode: options.businessMode,
930
+ ui_mode: runtimePayload && runtimePayload.ui_mode ? runtimePayload.ui_mode : options.uiMode,
931
+ dialogue_profile: options.dialogueProfile,
932
+ execution_mode: options.executionMode,
933
+ runtime_mode: runtimePayload && runtimePayload.runtime_mode ? runtimePayload.runtime_mode : options.runtimeMode,
934
+ runtime_environment: runtimePayload && runtimePayload.runtime_environment
935
+ ? runtimePayload.runtime_environment
936
+ : options.runtimeEnvironment,
937
+ decision: runtimeDecision,
938
+ reasons: Array.isArray(runtimePayload && runtimePayload.reasons)
939
+ ? runtimePayload.reasons
940
+ : [],
941
+ violation_codes: runtimeViolationCodes,
942
+ ui_mode_violation: runtimeUiModeViolationCodes.length > 0,
943
+ ui_mode_violation_codes: runtimeUiModeViolationCodes
944
+ };
945
+ const runtimeSignalsGlobalPath = resolvePath(cwd, artifacts.runtime_signals_global_jsonl);
946
+ await fs.ensureDir(path.dirname(runtimeSignalsGlobalPath));
947
+ await fs.appendFile(runtimeSignalsGlobalPath, `${JSON.stringify(runtimeSignalRecord)}\n`, 'utf8');
948
+ const runtimeSignalsSessionPath = resolvePath(cwd, artifacts.runtime_signals_jsonl);
949
+ await fs.ensureDir(path.dirname(runtimeSignalsSessionPath));
950
+ await fs.appendFile(runtimeSignalsSessionPath, `${JSON.stringify(runtimeSignalRecord)}\n`, 'utf8');
951
+
952
+ const authorizationTierArgs = [
953
+ '--execution-mode', options.executionMode,
954
+ '--dialogue-profile', options.dialogueProfile,
955
+ '--runtime-mode', options.runtimeMode,
956
+ '--runtime-environment', options.runtimeEnvironment,
957
+ '--out', resolvePath(cwd, artifacts.authorization_tier_json),
958
+ '--json'
959
+ ];
960
+ if (options.autoExecuteLowRisk) {
961
+ authorizationTierArgs.push('--auto-execute-low-risk');
962
+ }
963
+ if (options.liveApply) {
964
+ authorizationTierArgs.push('--live-apply');
965
+ }
966
+ if (options.authorizationTierPolicy) {
967
+ authorizationTierArgs.push('--policy', resolvePath(cwd, options.authorizationTierPolicy));
968
+ }
969
+ const authorizationTierResult = runScript({
970
+ label: 'interactive-authorization-tier-evaluate',
971
+ scriptPath: SCRIPT_AUTHORIZATION_TIER,
972
+ args: authorizationTierArgs,
973
+ cwd,
974
+ allowedExitCodes: [0]
975
+ });
976
+ const authorizationTierPayload = parseJsonOutput(authorizationTierResult.stdout, 'interactive-authorization-tier-evaluate');
977
+ steps.push(buildStep(
978
+ 'authorization_tier',
979
+ authorizationTierPayload,
980
+ buildCommandString(SCRIPT_AUTHORIZATION_TIER, authorizationTierArgs),
981
+ authorizationTierResult.exit_code
982
+ ));
983
+ const authorizationTierDecision = authorizationTierPayload && authorizationTierPayload.decision
984
+ ? authorizationTierPayload.decision
985
+ : 'deny';
986
+ const authorizationTierRequirements = authorizationTierPayload &&
987
+ authorizationTierPayload.requirements &&
988
+ typeof authorizationTierPayload.requirements === 'object'
989
+ ? authorizationTierPayload.requirements
990
+ : {};
991
+ const authorizationTierSignalRecord = {
992
+ event_type: 'interactive.authorization_tier.evaluated',
993
+ timestamp: new Date().toISOString(),
994
+ session_id: sessionId,
995
+ user_id: options.userId,
996
+ business_mode: options.businessMode,
997
+ execution_mode: options.executionMode,
998
+ runtime_mode: options.runtimeMode,
999
+ runtime_environment: options.runtimeEnvironment,
1000
+ dialogue_profile: options.dialogueProfile,
1001
+ decision: authorizationTierDecision,
1002
+ reasons: Array.isArray(authorizationTierPayload && authorizationTierPayload.reasons)
1003
+ ? authorizationTierPayload.reasons
1004
+ : [],
1005
+ requirements: authorizationTierRequirements,
1006
+ policy_source: authorizationTierPayload &&
1007
+ authorizationTierPayload.policy &&
1008
+ authorizationTierPayload.policy.source
1009
+ ? authorizationTierPayload.policy.source
1010
+ : null
1011
+ };
1012
+ const authorizationTierSignalsGlobalPath = resolvePath(cwd, artifacts.authorization_tier_signals_global_jsonl);
1013
+ await fs.ensureDir(path.dirname(authorizationTierSignalsGlobalPath));
1014
+ await fs.appendFile(authorizationTierSignalsGlobalPath, `${JSON.stringify(authorizationTierSignalRecord)}\n`, 'utf8');
1015
+ const authorizationTierSignalsSessionPath = resolvePath(cwd, artifacts.authorization_tier_signals_jsonl);
1016
+ await fs.ensureDir(path.dirname(authorizationTierSignalsSessionPath));
1017
+ await fs.appendFile(authorizationTierSignalsSessionPath, `${JSON.stringify(authorizationTierSignalRecord)}\n`, 'utf8');
1018
+
1019
+ const approvalInitArgs = [
1020
+ '--action', 'init',
1021
+ '--plan', resolvePath(cwd, artifacts.plan_json),
1022
+ '--state-file', resolvePath(cwd, artifacts.approval_state_json),
1023
+ '--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
1024
+ '--actor', options.approvalActor,
1025
+ '--comment', 'interactive loop init',
1026
+ '--force',
1027
+ '--json'
1028
+ ];
1029
+ if (options.approvalActorRole) {
1030
+ approvalInitArgs.push('--actor-role', options.approvalActorRole);
1031
+ }
1032
+ if (options.approvalRolePolicy) {
1033
+ approvalInitArgs.push('--role-policy', resolvePath(cwd, options.approvalRolePolicy));
1034
+ }
1035
+ if (options.authPasswordHash) {
1036
+ approvalInitArgs.push('--password-hash', options.authPasswordHash);
1037
+ }
1038
+ if (options.authPasswordEnv) {
1039
+ approvalInitArgs.push('--password-hash-env', options.authPasswordEnv);
1040
+ }
1041
+ const approvalInitResult = runScript({
1042
+ label: 'interactive-approval-workflow:init',
1043
+ scriptPath: SCRIPT_APPROVAL,
1044
+ args: approvalInitArgs,
1045
+ cwd,
1046
+ allowedExitCodes: [0]
1047
+ });
1048
+ const approvalInitPayload = parseJsonOutput(approvalInitResult.stdout, 'interactive-approval-workflow:init');
1049
+ steps.push(buildStep(
1050
+ 'approval_init',
1051
+ approvalInitPayload,
1052
+ buildCommandString(SCRIPT_APPROVAL, approvalInitArgs, ['--password-hash']),
1053
+ approvalInitResult.exit_code
1054
+ ));
1055
+
1056
+ if (!options.skipSubmit) {
1057
+ const approvalSubmitArgs = [
1058
+ '--action', 'submit',
1059
+ '--state-file', resolvePath(cwd, artifacts.approval_state_json),
1060
+ '--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
1061
+ '--actor', options.approvalActor,
1062
+ '--comment', 'interactive loop submit',
1063
+ '--json'
1064
+ ];
1065
+ if (options.approvalActorRole) {
1066
+ approvalSubmitArgs.push('--actor-role', options.approvalActorRole);
1067
+ }
1068
+ const approvalSubmitResult = runScript({
1069
+ label: 'interactive-approval-workflow:submit',
1070
+ scriptPath: SCRIPT_APPROVAL,
1071
+ args: approvalSubmitArgs,
1072
+ cwd,
1073
+ allowedExitCodes: [0]
1074
+ });
1075
+ const approvalSubmitPayload = parseJsonOutput(approvalSubmitResult.stdout, 'interactive-approval-workflow:submit');
1076
+ steps.push(buildStep('approval_submit', approvalSubmitPayload, buildCommandString(SCRIPT_APPROVAL, approvalSubmitArgs), approvalSubmitResult.exit_code));
1077
+ }
1078
+
1079
+ const canAutoLowRisk = shouldAutoLowRisk({
1080
+ gateDecision,
1081
+ riskLevel,
1082
+ dialogueDecision,
1083
+ runtimeDecision,
1084
+ runtimeAutoExecuteAllowed: runtimeRequirements.auto_execute_allowed === true,
1085
+ authorizationTierDecision,
1086
+ authorizationTierAutoExecuteAllowed: authorizationTierRequirements.auto_execute_allowed === true
1087
+ });
1088
+ if (options.autoExecuteLowRisk && !canAutoLowRisk && `${authorizationTierDecision || ''}`.trim().toLowerCase() !== 'allow') {
1089
+ steps.push(buildStep(
1090
+ 'authorization_apply_guard',
1091
+ {
1092
+ decision: 'blocked',
1093
+ reason: `authorization tier decision is "${authorizationTierDecision}"`,
1094
+ dialogue_profile: options.dialogueProfile,
1095
+ runtime_environment: options.runtimeEnvironment
1096
+ },
1097
+ 'authorization-tier-guard',
1098
+ 2
1099
+ ));
1100
+ }
1101
+
1102
+ if (options.autoApproveLowRisk && canAutoLowRisk) {
1103
+ const approvalApproveArgs = [
1104
+ '--action', 'approve',
1105
+ '--state-file', resolvePath(cwd, artifacts.approval_state_json),
1106
+ '--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
1107
+ '--actor', options.approverActor,
1108
+ '--comment', 'auto approve low-risk allow plan',
1109
+ '--json'
1110
+ ];
1111
+ if (options.approverActorRole) {
1112
+ approvalApproveArgs.push('--actor-role', options.approverActorRole);
1113
+ }
1114
+ const approvalApproveResult = runScript({
1115
+ label: 'interactive-approval-workflow:approve',
1116
+ scriptPath: SCRIPT_APPROVAL,
1117
+ args: approvalApproveArgs,
1118
+ cwd,
1119
+ allowedExitCodes: [0]
1120
+ });
1121
+ const approvalApprovePayload = parseJsonOutput(approvalApproveResult.stdout, 'interactive-approval-workflow:approve');
1122
+ steps.push(buildStep('approval_approve_auto', approvalApprovePayload, buildCommandString(SCRIPT_APPROVAL, approvalApproveArgs), approvalApproveResult.exit_code));
1123
+ }
1124
+
1125
+ let execution = {
1126
+ attempted: false,
1127
+ auto_triggered: false,
1128
+ blocked: false,
1129
+ result: null,
1130
+ decision: null,
1131
+ reason: null,
1132
+ execution_id: null,
1133
+ payload: null,
1134
+ exit_code: null
1135
+ };
1136
+
1137
+ if (options.autoExecuteLowRisk && canAutoLowRisk) {
1138
+ let runtimeExecutionBlockReason = null;
1139
+ if (options.liveApply && runtimeRequirements.allow_live_apply !== true) {
1140
+ runtimeExecutionBlockReason = `runtime environment "${options.runtimeEnvironment}" does not allow live apply`;
1141
+ } else if (
1142
+ options.liveApply &&
1143
+ !options.dryRun &&
1144
+ runtimeRequirements.require_dry_run_before_live_apply === true
1145
+ ) {
1146
+ runtimeExecutionBlockReason = `runtime environment "${options.runtimeEnvironment}" requires dry-run before live apply`;
1147
+ } else if (authorizationTierRequirements.require_password_for_apply === true && !options.authPassword) {
1148
+ runtimeExecutionBlockReason = `authorization tier requires one-time password for apply in "${options.runtimeEnvironment}"`;
1149
+ } else if (authorizationTierRequirements.require_role_policy === true && !options.approvalRolePolicy) {
1150
+ runtimeExecutionBlockReason = `authorization tier requires approval role policy for apply in "${options.runtimeEnvironment}"`;
1151
+ } else if (
1152
+ authorizationTierRequirements.require_distinct_actor_roles === true &&
1153
+ options.approvalActorRole &&
1154
+ options.approverActorRole &&
1155
+ options.approvalActorRole === options.approverActorRole
1156
+ ) {
1157
+ runtimeExecutionBlockReason = 'authorization tier requires distinct approval actor role and approver actor role';
1158
+ }
1159
+
1160
+ if (runtimeExecutionBlockReason) {
1161
+ execution = {
1162
+ attempted: true,
1163
+ auto_triggered: true,
1164
+ blocked: true,
1165
+ result: 'blocked',
1166
+ decision: 'runtime-blocked',
1167
+ reason: runtimeExecutionBlockReason,
1168
+ execution_id: null,
1169
+ payload: {
1170
+ mode: 'interactive-runtime-policy-evaluate',
1171
+ decision: 'blocked',
1172
+ reason: runtimeExecutionBlockReason
1173
+ },
1174
+ exit_code: 2
1175
+ };
1176
+ steps.push(buildStep(
1177
+ 'runtime_apply_guard',
1178
+ {
1179
+ decision: 'blocked',
1180
+ reason: runtimeExecutionBlockReason,
1181
+ runtime_mode: options.runtimeMode,
1182
+ runtime_environment: options.runtimeEnvironment
1183
+ },
1184
+ 'runtime-guard',
1185
+ 2
1186
+ ));
1187
+ } else {
1188
+ const approvalExecuteArgs = [
1189
+ '--action', 'execute',
1190
+ '--state-file', resolvePath(cwd, artifacts.approval_state_json),
1191
+ '--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
1192
+ '--actor', options.approverActor,
1193
+ '--comment', 'auto execute low-risk allow plan',
1194
+ '--json'
1195
+ ];
1196
+ if (options.approverActorRole) {
1197
+ approvalExecuteArgs.push('--actor-role', options.approverActorRole);
1198
+ }
1199
+ if (options.authPassword) {
1200
+ approvalExecuteArgs.push('--password', options.authPassword);
1201
+ }
1202
+ const approvalExecuteResult = runScript({
1203
+ label: 'interactive-approval-workflow:execute',
1204
+ scriptPath: SCRIPT_APPROVAL,
1205
+ args: approvalExecuteArgs,
1206
+ cwd,
1207
+ allowedExitCodes: [0, 2]
1208
+ });
1209
+ const approvalExecutePayload = parseJsonOutput(approvalExecuteResult.stdout, 'interactive-approval-workflow:execute');
1210
+ steps.push(buildStep(
1211
+ 'approval_execute_auto',
1212
+ approvalExecutePayload,
1213
+ buildCommandString(SCRIPT_APPROVAL, approvalExecuteArgs, ['--password']),
1214
+ approvalExecuteResult.exit_code
1215
+ ));
1216
+
1217
+ if (approvalExecuteResult.exit_code === 2 || approvalExecutePayload.decision === 'blocked') {
1218
+ execution = {
1219
+ attempted: true,
1220
+ auto_triggered: true,
1221
+ blocked: true,
1222
+ result: 'blocked',
1223
+ decision: 'approval-blocked',
1224
+ reason: approvalExecutePayload.reason || 'approval execute blocked',
1225
+ execution_id: null,
1226
+ payload: approvalExecutePayload,
1227
+ exit_code: approvalExecuteResult.exit_code
1228
+ };
1229
+ } else {
1230
+ const adapterArgs = [
1231
+ '--action', 'low-risk-apply',
1232
+ '--plan', resolvePath(cwd, artifacts.plan_json),
1233
+ '--out', resolvePath(cwd, artifacts.adapter_json),
1234
+ '--json'
1235
+ ];
1236
+ if (options.policy) {
1237
+ adapterArgs.push('--policy', resolvePath(cwd, options.policy));
1238
+ }
1239
+ if (options.catalog) {
1240
+ adapterArgs.push('--catalog', resolvePath(cwd, options.catalog));
1241
+ }
1242
+ if (options.moquiConfig) {
1243
+ adapterArgs.push('--moqui-config', resolvePath(cwd, options.moquiConfig));
1244
+ }
1245
+ if (options.liveApply) {
1246
+ adapterArgs.push('--live-apply');
1247
+ }
1248
+ if (!options.dryRun) {
1249
+ adapterArgs.push('--no-dry-run');
1250
+ }
1251
+ if (options.allowSuggestionApply) {
1252
+ adapterArgs.push('--allow-suggestion-apply');
1253
+ }
1254
+
1255
+ const adapterResult = runScript({
1256
+ label: 'interactive-moqui-adapter:low-risk-apply',
1257
+ scriptPath: SCRIPT_ADAPTER,
1258
+ args: adapterArgs,
1259
+ cwd,
1260
+ allowedExitCodes: [0, 2]
1261
+ });
1262
+ const adapterPayload = parseJsonOutput(adapterResult.stdout, 'interactive-moqui-adapter:low-risk-apply');
1263
+ const record = adapterPayload &&
1264
+ adapterPayload.payload &&
1265
+ adapterPayload.payload.execution_record &&
1266
+ typeof adapterPayload.payload.execution_record === 'object'
1267
+ ? adapterPayload.payload.execution_record
1268
+ : null;
1269
+
1270
+ execution = {
1271
+ attempted: true,
1272
+ auto_triggered: true,
1273
+ blocked: adapterResult.exit_code === 2,
1274
+ result: record ? record.result : null,
1275
+ decision: adapterPayload && adapterPayload.payload ? adapterPayload.payload.decision : null,
1276
+ reason: adapterPayload && adapterPayload.payload ? adapterPayload.payload.reason || null : null,
1277
+ execution_id: record ? record.execution_id : null,
1278
+ payload: adapterPayload,
1279
+ exit_code: adapterResult.exit_code
1280
+ };
1281
+ steps.push(buildStep('adapter_low_risk_apply_auto', adapterPayload, buildCommandString(SCRIPT_ADAPTER, adapterArgs), adapterResult.exit_code));
1282
+
1283
+ if (adapterResult.exit_code === 0) {
1284
+ const approvalVerifyArgs = [
1285
+ '--action', 'verify',
1286
+ '--state-file', resolvePath(cwd, artifacts.approval_state_json),
1287
+ '--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
1288
+ '--actor', options.approverActor,
1289
+ '--comment', 'auto verify after successful low-risk apply',
1290
+ '--json'
1291
+ ];
1292
+ if (options.approverActorRole) {
1293
+ approvalVerifyArgs.push('--actor-role', options.approverActorRole);
1294
+ }
1295
+ const approvalVerifyResult = runScript({
1296
+ label: 'interactive-approval-workflow:verify',
1297
+ scriptPath: SCRIPT_APPROVAL,
1298
+ args: approvalVerifyArgs,
1299
+ cwd,
1300
+ allowedExitCodes: [0, 2]
1301
+ });
1302
+ const approvalVerifyPayload = parseJsonOutput(approvalVerifyResult.stdout, 'interactive-approval-workflow:verify');
1303
+ steps.push(buildStep('approval_verify_auto', approvalVerifyPayload, buildCommandString(SCRIPT_APPROVAL, approvalVerifyArgs), approvalVerifyResult.exit_code));
1304
+ }
1305
+ }
1306
+ }
1307
+ }
1308
+
1309
+ const approvalStatusArgs = [
1310
+ '--action', 'status',
1311
+ '--state-file', resolvePath(cwd, artifacts.approval_state_json),
1312
+ '--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
1313
+ '--json'
1314
+ ];
1315
+ const approvalStatusResult = runScript({
1316
+ label: 'interactive-approval-workflow:status',
1317
+ scriptPath: SCRIPT_APPROVAL,
1318
+ args: approvalStatusArgs,
1319
+ cwd,
1320
+ allowedExitCodes: [0]
1321
+ });
1322
+ const approvalStatusPayload = parseJsonOutput(approvalStatusResult.stdout, 'interactive-approval-workflow:status');
1323
+ steps.push(buildStep('approval_status', approvalStatusPayload, buildCommandString(SCRIPT_APPROVAL, approvalStatusArgs), approvalStatusResult.exit_code));
1324
+
1325
+ const approvalState = approvalStatusPayload && approvalStatusPayload.state ? approvalStatusPayload.state : {};
1326
+ const approvalStatus = approvalState && approvalState.status ? approvalState.status : null;
1327
+ const intentRecord = intentPayload && intentPayload.intent && typeof intentPayload.intent === 'object'
1328
+ ? intentPayload.intent
1329
+ : {};
1330
+ const planRecord = planPayload && planPayload.plan && typeof planPayload.plan === 'object'
1331
+ ? planPayload.plan
1332
+ : {};
1333
+ const contextRef = intentRecord.context_ref && typeof intentRecord.context_ref === 'object'
1334
+ ? intentRecord.context_ref
1335
+ : {};
1336
+
1337
+ const feedback = {
1338
+ requested: options.feedbackScore != null,
1339
+ logged: false,
1340
+ score: options.feedbackScore,
1341
+ feedback_id: null,
1342
+ global_file: artifacts.feedback_global_jsonl,
1343
+ session_file: artifacts.feedback_jsonl,
1344
+ payload: null,
1345
+ exit_code: null
1346
+ };
1347
+
1348
+ if (options.feedbackScore != null) {
1349
+ const feedbackArgs = [
1350
+ '--score', String(options.feedbackScore),
1351
+ '--user-id', options.userId,
1352
+ '--session-id', sessionId,
1353
+ '--feedback-file', globalFeedbackPath,
1354
+ '--channel', options.feedbackChannel,
1355
+ '--json'
1356
+ ];
1357
+ if (options.feedbackComment) {
1358
+ feedbackArgs.push('--comment', options.feedbackComment);
1359
+ }
1360
+ if (options.feedbackTags.length > 0) {
1361
+ feedbackArgs.push('--tags', options.feedbackTags.join(','));
1362
+ }
1363
+ if (intentRecord.intent_id) {
1364
+ feedbackArgs.push('--intent-id', intentRecord.intent_id);
1365
+ }
1366
+ if (planRecord.plan_id) {
1367
+ feedbackArgs.push('--plan-id', planRecord.plan_id);
1368
+ }
1369
+ if (execution.execution_id) {
1370
+ feedbackArgs.push('--execution-id', execution.execution_id);
1371
+ }
1372
+ if (contextRef.product) {
1373
+ feedbackArgs.push('--product', `${contextRef.product}`);
1374
+ }
1375
+ if (contextRef.module) {
1376
+ feedbackArgs.push('--module', `${contextRef.module}`);
1377
+ }
1378
+ if (contextRef.page) {
1379
+ feedbackArgs.push('--page', `${contextRef.page}`);
1380
+ }
1381
+ if (contextRef.scene_id) {
1382
+ feedbackArgs.push('--scene-id', `${contextRef.scene_id}`);
1383
+ }
1384
+ const feedbackResult = runScript({
1385
+ label: 'interactive-feedback-log',
1386
+ scriptPath: SCRIPT_FEEDBACK,
1387
+ args: feedbackArgs,
1388
+ cwd,
1389
+ allowedExitCodes: [0]
1390
+ });
1391
+ const feedbackPayload = parseJsonOutput(feedbackResult.stdout, 'interactive-feedback-log');
1392
+ const feedbackRecord = feedbackPayload && feedbackPayload.record && typeof feedbackPayload.record === 'object'
1393
+ ? feedbackPayload.record
1394
+ : null;
1395
+ if (feedbackRecord) {
1396
+ await fs.ensureDir(path.dirname(resolvePath(cwd, artifacts.feedback_jsonl)));
1397
+ await fs.appendFile(
1398
+ resolvePath(cwd, artifacts.feedback_jsonl),
1399
+ `${JSON.stringify(feedbackRecord)}\n`,
1400
+ 'utf8'
1401
+ );
1402
+ }
1403
+ steps.push(buildStep('feedback_log', feedbackPayload, buildCommandString(SCRIPT_FEEDBACK, feedbackArgs), feedbackResult.exit_code));
1404
+ feedback.logged = true;
1405
+ feedback.payload = feedbackPayload;
1406
+ feedback.exit_code = feedbackResult.exit_code;
1407
+ feedback.feedback_id = feedbackRecord
1408
+ ? feedbackRecord.feedback_id || null
1409
+ : null;
1410
+ }
1411
+
1412
+ const workOrderArgs = [
1413
+ '--plan', resolvePath(cwd, artifacts.plan_json),
1414
+ '--dialogue', resolvePath(cwd, artifacts.dialogue_json),
1415
+ '--intent', resolvePath(cwd, artifacts.intent_json),
1416
+ '--gate', resolvePath(cwd, artifacts.gate_json),
1417
+ '--runtime', resolvePath(cwd, artifacts.runtime_json),
1418
+ '--authorization-tier', resolvePath(cwd, artifacts.authorization_tier_json),
1419
+ '--approval-state', resolvePath(cwd, artifacts.approval_state_json),
1420
+ '--session-id', sessionId,
1421
+ '--runtime-mode', options.runtimeMode,
1422
+ '--runtime-environment', options.runtimeEnvironment,
1423
+ '--out', resolvePath(cwd, artifacts.work_order_json),
1424
+ '--markdown-out', resolvePath(cwd, artifacts.work_order_md),
1425
+ '--json'
1426
+ ];
1427
+ if (options.goal) {
1428
+ workOrderArgs.push('--goal', options.goal);
1429
+ }
1430
+ if (execution.attempted) {
1431
+ workOrderArgs.push('--execution-attempted');
1432
+ }
1433
+ if (execution.blocked) {
1434
+ workOrderArgs.push('--execution-blocked');
1435
+ }
1436
+ if (execution.result) {
1437
+ workOrderArgs.push('--execution-result', execution.result);
1438
+ }
1439
+ if (execution.reason) {
1440
+ workOrderArgs.push('--execution-reason', execution.reason);
1441
+ }
1442
+ if (execution.execution_id) {
1443
+ workOrderArgs.push('--execution-id', execution.execution_id);
1444
+ }
1445
+ const workOrderResult = runScript({
1446
+ label: 'interactive-work-order-build',
1447
+ scriptPath: SCRIPT_WORK_ORDER,
1448
+ args: workOrderArgs,
1449
+ cwd,
1450
+ allowedExitCodes: [0]
1451
+ });
1452
+ const workOrderPayload = parseJsonOutput(workOrderResult.stdout, 'interactive-work-order-build');
1453
+ steps.push(buildStep(
1454
+ 'work_order',
1455
+ workOrderPayload,
1456
+ buildCommandString(SCRIPT_WORK_ORDER, workOrderArgs),
1457
+ workOrderResult.exit_code
1458
+ ));
1459
+
1460
+ const summaryStatus = buildSummaryStatus({
1461
+ dialogueDecision,
1462
+ gateDecision,
1463
+ runtimeDecision,
1464
+ authorizationTierDecision,
1465
+ executionAttempted: execution.attempted,
1466
+ executionBlocked: execution.blocked,
1467
+ executionResult: execution.result
1468
+ });
1469
+ const executionBlockReasonCategory = detectExecutionBlockReasonCategory(execution);
1470
+ const executionBlockRemediationHint = buildExecutionBlockRemediationHint({
1471
+ blockReasonCategory: executionBlockReasonCategory,
1472
+ executionReason: execution.reason
1473
+ });
1474
+
1475
+ const payload = {
1476
+ mode: 'interactive-customization-loop',
1477
+ generated_at: new Date().toISOString(),
1478
+ session_id: sessionId,
1479
+ input: {
1480
+ context: toRelative(cwd, contextPath),
1481
+ goal: options.goal || null,
1482
+ goal_file: options.goalFile ? toRelative(cwd, resolvePath(cwd, options.goalFile)) : null,
1483
+ user_id: options.userId,
1484
+ execution_mode: options.executionMode
1485
+ },
1486
+ options: {
1487
+ business_mode: options.businessMode,
1488
+ business_mode_policy: options.businessModeState
1489
+ ? toRelative(cwd, options.businessModeState.policy_path)
1490
+ : toRelative(cwd, resolvePath(cwd, options.businessModePolicy)),
1491
+ business_mode_policy_source: options.businessModeState ? options.businessModeState.policy_source : 'unknown',
1492
+ allow_mode_override: options.allowModeOverride === true,
1493
+ policy: options.policy ? toRelative(cwd, resolvePath(cwd, options.policy)) : null,
1494
+ catalog: options.catalog ? toRelative(cwd, resolvePath(cwd, options.catalog)) : null,
1495
+ moqui_config: options.moquiConfig ? toRelative(cwd, resolvePath(cwd, options.moquiConfig)) : null,
1496
+ approval_actor: options.approvalActor,
1497
+ approval_actor_role: options.approvalActorRole,
1498
+ approver_actor: options.approverActor,
1499
+ approver_actor_role: options.approverActorRole,
1500
+ approval_role_policy: options.approvalRolePolicy ? toRelative(cwd, resolvePath(cwd, options.approvalRolePolicy)) : null,
1501
+ skip_submit: options.skipSubmit,
1502
+ auto_approve_low_risk: options.autoApproveLowRisk,
1503
+ auto_execute_low_risk: options.autoExecuteLowRisk,
1504
+ allow_suggestion_apply: options.allowSuggestionApply,
1505
+ live_apply: options.liveApply,
1506
+ dry_run: options.dryRun,
1507
+ feedback_score: options.feedbackScore,
1508
+ feedback_comment: options.feedbackComment,
1509
+ feedback_tags: options.feedbackTags,
1510
+ feedback_channel: options.feedbackChannel,
1511
+ dialogue_policy: options.dialoguePolicy ? toRelative(cwd, resolvePath(cwd, options.dialoguePolicy)) : null,
1512
+ dialogue_profile: options.dialogueProfile,
1513
+ ui_mode: options.uiMode,
1514
+ runtime_mode: options.runtimeMode,
1515
+ runtime_environment: options.runtimeEnvironment,
1516
+ runtime_policy: options.runtimePolicy ? toRelative(cwd, resolvePath(cwd, options.runtimePolicy)) : null,
1517
+ authorization_tier_policy: options.authorizationTierPolicy
1518
+ ? toRelative(cwd, resolvePath(cwd, options.authorizationTierPolicy))
1519
+ : null,
1520
+ auth_password_hash: options.authPasswordHash ? '***' : null,
1521
+ auth_password_env: options.authPasswordEnv || DEFAULT_AUTH_PASSWORD_HASH_ENV
1522
+ },
1523
+ dialogue: {
1524
+ decision: dialogueDecision,
1525
+ profile: dialoguePayload && dialoguePayload.policy ? dialoguePayload.policy.active_profile || null : null,
1526
+ ui_mode: dialoguePayload &&
1527
+ dialoguePayload.authorization_dialogue &&
1528
+ dialoguePayload.authorization_dialogue.context &&
1529
+ dialoguePayload.authorization_dialogue.context.ui_mode
1530
+ ? dialoguePayload.authorization_dialogue.context.ui_mode
1531
+ : options.uiMode,
1532
+ reasons: Array.isArray(dialoguePayload && dialoguePayload.reasons) ? dialoguePayload.reasons : [],
1533
+ clarification_questions: Array.isArray(dialoguePayload && dialoguePayload.clarification_questions)
1534
+ ? dialoguePayload.clarification_questions
1535
+ : [],
1536
+ authorization_dialogue: dialoguePayload &&
1537
+ dialoguePayload.authorization_dialogue &&
1538
+ typeof dialoguePayload.authorization_dialogue === 'object'
1539
+ ? dialoguePayload.authorization_dialogue
1540
+ : null
1541
+ },
1542
+ gate: {
1543
+ decision: gateDecision,
1544
+ risk_level: riskLevel,
1545
+ reasons: Array.isArray(gatePayload && gatePayload.reasons) ? gatePayload.reasons : []
1546
+ },
1547
+ runtime: {
1548
+ decision: runtimeDecision,
1549
+ ui_mode: runtimePayload && runtimePayload.ui_mode ? runtimePayload.ui_mode : options.uiMode,
1550
+ mode: runtimePayload && runtimePayload.runtime_mode ? runtimePayload.runtime_mode : options.runtimeMode,
1551
+ environment: runtimePayload && runtimePayload.runtime_environment ? runtimePayload.runtime_environment : options.runtimeEnvironment,
1552
+ reasons: Array.isArray(runtimePayload && runtimePayload.reasons) ? runtimePayload.reasons : [],
1553
+ violations: Array.isArray(runtimePayload && runtimePayload.violations) ? runtimePayload.violations : [],
1554
+ requirements: runtimeRequirements
1555
+ },
1556
+ authorization_tier: {
1557
+ decision: authorizationTierDecision,
1558
+ profile: authorizationTierPayload &&
1559
+ authorizationTierPayload.context &&
1560
+ authorizationTierPayload.context.dialogue_profile
1561
+ ? authorizationTierPayload.context.dialogue_profile
1562
+ : options.dialogueProfile,
1563
+ runtime_environment: authorizationTierPayload &&
1564
+ authorizationTierPayload.context &&
1565
+ authorizationTierPayload.context.runtime_environment
1566
+ ? authorizationTierPayload.context.runtime_environment
1567
+ : options.runtimeEnvironment,
1568
+ reasons: Array.isArray(authorizationTierPayload && authorizationTierPayload.reasons)
1569
+ ? authorizationTierPayload.reasons
1570
+ : [],
1571
+ requirements: authorizationTierRequirements
1572
+ },
1573
+ approval: {
1574
+ workflow_id: approvalState.workflow_id || null,
1575
+ status: approvalStatus,
1576
+ approval_required: approvalState.approval_required === true,
1577
+ approvals: approvalState.approvals || {},
1578
+ authorization: approvalStatusPayload && approvalStatusPayload.authorization
1579
+ ? approvalStatusPayload.authorization
1580
+ : {}
1581
+ },
1582
+ execution,
1583
+ summary: {
1584
+ status: summaryStatus,
1585
+ business_mode: options.businessMode,
1586
+ ui_mode: options.uiMode,
1587
+ dialogue_authorization_decision: dialoguePayload &&
1588
+ dialoguePayload.authorization_dialogue &&
1589
+ dialoguePayload.authorization_dialogue.decision
1590
+ ? dialoguePayload.authorization_dialogue.decision
1591
+ : null,
1592
+ authorization_tier_decision: authorizationTierDecision,
1593
+ execution_block_reason_category: executionBlockReasonCategory,
1594
+ execution_block_remediation_hint: executionBlockRemediationHint,
1595
+ next_actions: buildNextActions({
1596
+ dialogueDecision,
1597
+ gateDecision,
1598
+ runtimeDecision,
1599
+ authorizationTierDecision,
1600
+ riskLevel,
1601
+ runtime: {
1602
+ ui_mode: runtimePayload && runtimePayload.ui_mode ? runtimePayload.ui_mode : options.uiMode,
1603
+ mode: runtimePayload && runtimePayload.runtime_mode ? runtimePayload.runtime_mode : options.runtimeMode,
1604
+ environment: runtimePayload && runtimePayload.runtime_environment ? runtimePayload.runtime_environment : options.runtimeEnvironment
1605
+ },
1606
+ authorizationTier: {
1607
+ profile: authorizationTierPayload &&
1608
+ authorizationTierPayload.context &&
1609
+ authorizationTierPayload.context.dialogue_profile
1610
+ ? authorizationTierPayload.context.dialogue_profile
1611
+ : options.dialogueProfile,
1612
+ requirements: authorizationTierRequirements
1613
+ },
1614
+ autoExecuteTriggered: execution.auto_triggered === true,
1615
+ executionPayload: execution.payload && execution.payload.payload ? execution.payload.payload : null,
1616
+ executionBlockReasonCategory,
1617
+ executionReason: execution.reason,
1618
+ approvalStatus,
1619
+ feedbackLogged: feedback.logged,
1620
+ artifacts
1621
+ })
1622
+ },
1623
+ work_order: workOrderPayload && workOrderPayload.work_order
1624
+ ? workOrderPayload.work_order
1625
+ : null,
1626
+ feedback,
1627
+ artifacts,
1628
+ steps
1629
+ };
1630
+
1631
+ await fs.ensureDir(path.dirname(summaryOutPath));
1632
+ await fs.writeJson(summaryOutPath, payload, { spaces: 2 });
1633
+
1634
+ if (options.json) {
1635
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
1636
+ } else {
1637
+ process.stdout.write(`Interactive customization loop completed: ${payload.summary.status}\n`);
1638
+ process.stdout.write(`- Session: ${sessionId}\n`);
1639
+ process.stdout.write(`- Gate: ${gateDecision}\n`);
1640
+ process.stdout.write(`- Runtime: ${runtimeDecision}\n`);
1641
+ process.stdout.write(`- Summary: ${toRelative(cwd, summaryOutPath)}\n`);
1642
+ }
1643
+
1644
+ if (options.failOnGateDeny && `${gateDecision}`.trim().toLowerCase() === 'deny') {
1645
+ process.exitCode = 2;
1646
+ } else if (options.failOnDialogueDeny && `${dialogueDecision}`.trim().toLowerCase() === 'deny') {
1647
+ process.exitCode = 2;
1648
+ } else if (options.failOnGateNonAllow && `${gateDecision}`.trim().toLowerCase() !== 'allow') {
1649
+ process.exitCode = 2;
1650
+ } else if (options.failOnRuntimeNonAllow && `${runtimeDecision}`.trim().toLowerCase() !== 'allow') {
1651
+ process.exitCode = 2;
1652
+ } else if (options.failOnExecuteBlocked && execution.attempted && execution.blocked) {
1653
+ process.exitCode = 2;
1654
+ }
1655
+ }
1656
+
1657
+ if (require.main === module) {
1658
+ main().catch((error) => {
1659
+ console.error(`Interactive customization loop failed: ${error.message}`);
1660
+ process.exit(1);
1661
+ });
1662
+ }
1663
+
1664
+ module.exports = {
1665
+ DEFAULT_OUT_DIR,
1666
+ DEFAULT_USER_ID,
1667
+ DEFAULT_APPROVAL_ACTOR,
1668
+ DEFAULT_FEEDBACK_CHANNEL,
1669
+ DEFAULT_RUNTIME_MODE,
1670
+ DEFAULT_RUNTIME_ENVIRONMENT,
1671
+ DEFAULT_DIALOGUE_PROFILE,
1672
+ FEEDBACK_CHANNELS,
1673
+ RUNTIME_MODES,
1674
+ RUNTIME_ENVIRONMENTS,
1675
+ DIALOGUE_PROFILES,
1676
+ parseArgs,
1677
+ resolvePath,
1678
+ normalizeSessionId,
1679
+ parseJsonOutput,
1680
+ buildCommandString,
1681
+ runScript,
1682
+ toRelative,
1683
+ buildStep,
1684
+ shouldAutoLowRisk,
1685
+ buildSummaryStatus,
1686
+ detectExecutionBlockReasonCategory,
1687
+ buildExecutionBlockRemediationHint,
1688
+ buildNextActions,
1689
+ main
1690
+ };