scene-capability-engine 3.6.45 → 3.6.47

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 (72) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +1 -0
  3. package/README.zh.md +1 -0
  4. package/docs/agent-runtime/symbol-evidence.schema.json +1 -1
  5. package/docs/command-reference.md +8 -0
  6. package/docs/interactive-customization/dialogue-governance-policy-baseline.json +4 -1
  7. package/docs/interactive-customization/embedded-assistant-authorization-dialogue-rules.md +5 -0
  8. package/docs/releases/README.md +2 -0
  9. package/docs/releases/v3.6.46.md +23 -0
  10. package/docs/releases/v3.6.47.md +23 -0
  11. package/docs/sce-business-mode-map.md +2 -1
  12. package/docs/sce-capability-matrix-e2e-example.md +2 -1
  13. package/docs/security-governance-default-baseline.md +2 -0
  14. package/docs/starter-kit/README.md +3 -0
  15. package/docs/zh/releases/README.md +2 -0
  16. package/docs/zh/releases/v3.6.46.md +23 -0
  17. package/docs/zh/releases/v3.6.47.md +23 -0
  18. package/lib/workspace/takeover-baseline.js +293 -1
  19. package/package.json +6 -2
  20. package/scripts/auto-strategy-router.js +231 -0
  21. package/scripts/capability-mapping-report.js +339 -0
  22. package/scripts/check-branding-consistency.js +140 -0
  23. package/scripts/check-sce-tracking.js +54 -0
  24. package/scripts/check-skip-allowlist.js +94 -0
  25. package/scripts/clarification-first-audit.js +322 -0
  26. package/scripts/errorbook-registry-health-gate.js +172 -0
  27. package/scripts/errorbook-release-gate.js +132 -0
  28. package/scripts/failure-attribution-repair.js +317 -0
  29. package/scripts/git-managed-gate.js +464 -0
  30. package/scripts/interactive-approval-event-projection.js +400 -0
  31. package/scripts/interactive-approval-workflow.js +829 -0
  32. package/scripts/interactive-authorization-tier-evaluate.js +413 -0
  33. package/scripts/interactive-change-plan-gate.js +225 -0
  34. package/scripts/interactive-context-bridge.js +617 -0
  35. package/scripts/interactive-customization-loop.js +1690 -0
  36. package/scripts/interactive-dialogue-governance.js +873 -0
  37. package/scripts/interactive-feedback-log.js +253 -0
  38. package/scripts/interactive-flow-smoke.js +238 -0
  39. package/scripts/interactive-flow.js +1059 -0
  40. package/scripts/interactive-governance-report.js +1112 -0
  41. package/scripts/interactive-intent-build.js +707 -0
  42. package/scripts/interactive-loop-smoke.js +215 -0
  43. package/scripts/interactive-moqui-adapter.js +304 -0
  44. package/scripts/interactive-plan-build.js +426 -0
  45. package/scripts/interactive-runtime-policy-evaluate.js +495 -0
  46. package/scripts/interactive-work-order-build.js +552 -0
  47. package/scripts/matrix-regression-gate.js +167 -0
  48. package/scripts/moqui-core-regression-suite.js +397 -0
  49. package/scripts/moqui-lexicon-audit.js +651 -0
  50. package/scripts/moqui-matrix-remediation-phased-runner.js +865 -0
  51. package/scripts/moqui-matrix-remediation-queue.js +852 -0
  52. package/scripts/moqui-metadata-extract.js +1340 -0
  53. package/scripts/moqui-rebuild-gate.js +167 -0
  54. package/scripts/moqui-release-summary.js +729 -0
  55. package/scripts/moqui-standard-rebuild.js +1370 -0
  56. package/scripts/moqui-template-baseline-report.js +682 -0
  57. package/scripts/npm-package-runtime-asset-check.js +221 -0
  58. package/scripts/problem-closure-gate.js +441 -0
  59. package/scripts/release-asset-integrity-check.js +216 -0
  60. package/scripts/release-asset-nonempty-normalize.js +166 -0
  61. package/scripts/release-drift-evaluate.js +223 -0
  62. package/scripts/release-drift-signals.js +255 -0
  63. package/scripts/release-governance-snapshot-export.js +132 -0
  64. package/scripts/release-ops-weekly-summary.js +934 -0
  65. package/scripts/release-risk-remediation-bundle.js +315 -0
  66. package/scripts/release-weekly-ops-gate.js +423 -0
  67. package/scripts/state-migration-reconciliation-gate.js +110 -0
  68. package/scripts/state-storage-tiering-audit.js +337 -0
  69. package/scripts/steering-content-audit.js +393 -0
  70. package/scripts/symbol-evidence-locate.js +370 -0
  71. package/template/.sce/README.md +1 -0
  72. package/template/.sce/steering/CORE_PRINCIPLES.md +25 -0
@@ -0,0 +1,464 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('path');
5
+ const { execSync, spawnSync } = require('child_process');
6
+
7
+ function normalizeText(value) {
8
+ if (typeof value !== 'string') {
9
+ return '';
10
+ }
11
+ return value.trim();
12
+ }
13
+
14
+ function parseBoolean(value, fallback = false) {
15
+ const normalized = normalizeText(`${value || ''}`).toLowerCase();
16
+ if (!normalized) {
17
+ return fallback;
18
+ }
19
+ if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) {
20
+ return true;
21
+ }
22
+ if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) {
23
+ return false;
24
+ }
25
+ return fallback;
26
+ }
27
+
28
+ function parseArgs(argv = [], env = process.env) {
29
+ const ciDetected = parseBoolean(env.GITHUB_ACTIONS, false) || parseBoolean(env.CI, false);
30
+ const options = {
31
+ projectPath: process.cwd(),
32
+ failOnViolation: false,
33
+ allowNoRemote: parseBoolean(env.SCE_GIT_MANAGEMENT_ALLOW_NO_REMOTE, true),
34
+ allowUntracked: parseBoolean(env.SCE_GIT_MANAGEMENT_ALLOW_UNTRACKED, false),
35
+ targetHosts: normalizeText(env.SCE_GIT_MANAGEMENT_TARGET_HOSTS || 'github.com,gitlab.com')
36
+ .split(',')
37
+ .map((item) => normalizeText(item).toLowerCase())
38
+ .filter(Boolean),
39
+ ciContext: ciDetected,
40
+ strictCi: parseBoolean(env.SCE_GIT_MANAGEMENT_STRICT_CI, false),
41
+ json: false,
42
+ help: false
43
+ };
44
+
45
+ for (let index = 0; index < argv.length; index += 1) {
46
+ const token = argv[index];
47
+ const next = argv[index + 1];
48
+
49
+ if (token === '--project-path' && next) {
50
+ options.projectPath = path.resolve(next);
51
+ index += 1;
52
+ } else if (token === '--fail-on-violation') {
53
+ options.failOnViolation = true;
54
+ } else if (token === '--allow-no-remote') {
55
+ options.allowNoRemote = true;
56
+ } else if (token === '--no-allow-no-remote') {
57
+ options.allowNoRemote = false;
58
+ } else if (token === '--allow-untracked') {
59
+ options.allowUntracked = true;
60
+ } else if (token === '--no-allow-untracked') {
61
+ options.allowUntracked = false;
62
+ } else if (token === '--target-hosts' && next) {
63
+ options.targetHosts = `${next}`
64
+ .split(',')
65
+ .map((item) => normalizeText(item).toLowerCase())
66
+ .filter(Boolean);
67
+ index += 1;
68
+ } else if (token === '--ci-context') {
69
+ options.ciContext = true;
70
+ } else if (token === '--no-ci-context') {
71
+ options.ciContext = false;
72
+ } else if (token === '--strict-ci') {
73
+ options.strictCi = true;
74
+ } else if (token === '--no-strict-ci') {
75
+ options.strictCi = false;
76
+ } else if (token === '--json') {
77
+ options.json = true;
78
+ } else if (token === '--help' || token === '-h') {
79
+ options.help = true;
80
+ }
81
+ }
82
+
83
+ if (options.targetHosts.length === 0) {
84
+ options.targetHosts = ['github.com', 'gitlab.com'];
85
+ }
86
+ return options;
87
+ }
88
+
89
+ function printHelp() {
90
+ const lines = [
91
+ 'Usage: node scripts/git-managed-gate.js [options]',
92
+ '',
93
+ 'Options:',
94
+ ' --project-path <path> Project path (default: cwd)',
95
+ ' --fail-on-violation Exit with code 2 when violations exist',
96
+ ' --allow-no-remote Allow pass when no GitHub/GitLab remote is configured',
97
+ ' --no-allow-no-remote Fail when no GitHub/GitLab remote is configured',
98
+ ' --allow-untracked Allow untracked files in worktree (tracked changes still fail)',
99
+ ' --no-allow-untracked Fail when untracked files exist (default)',
100
+ ' --target-hosts <csv> Remote host match list (default: github.com,gitlab.com)',
101
+ ' --ci-context Treat current run as CI context (default from CI/GITHUB_ACTIONS env)',
102
+ ' --no-ci-context Force local mode even if CI env exists',
103
+ ' --strict-ci In CI, enforce local-level branch/upstream sync checks',
104
+ ' --no-strict-ci In CI, relax detached/upstream sync checks (default)',
105
+ ' --json Print JSON payload',
106
+ ' -h, --help Show help'
107
+ ];
108
+ process.stdout.write(`${lines.join('\n')}\n`);
109
+ }
110
+
111
+ let cachedWindowsGitCommand = null;
112
+
113
+ function quoteShellArg(value) {
114
+ return JSON.stringify(String(value));
115
+ }
116
+
117
+ function resolveWindowsGitCommand(projectPath) {
118
+ if (cachedWindowsGitCommand) {
119
+ return cachedWindowsGitCommand;
120
+ }
121
+
122
+ const whereResult = spawnSync('where', ['git'], {
123
+ cwd: projectPath,
124
+ encoding: 'utf8',
125
+ windowsHide: true
126
+ });
127
+
128
+ if (whereResult.status === 0) {
129
+ const candidate = `${whereResult.stdout || ''}`
130
+ .split(/\r?\n/)
131
+ .map((line) => line.trim())
132
+ .find(Boolean);
133
+ if (candidate) {
134
+ cachedWindowsGitCommand = candidate;
135
+ return cachedWindowsGitCommand;
136
+ }
137
+ }
138
+
139
+ cachedWindowsGitCommand = 'git';
140
+ return cachedWindowsGitCommand;
141
+ }
142
+
143
+ function runGit(projectPath, args) {
144
+ if (process.platform === 'win32') {
145
+ const gitCommand = resolveWindowsGitCommand(projectPath);
146
+ const commandString = [quoteShellArg(gitCommand), ...args.map(quoteShellArg)].join(' ');
147
+
148
+ try {
149
+ const stdout = execSync(commandString, {
150
+ cwd: projectPath,
151
+ encoding: 'utf8',
152
+ windowsHide: true
153
+ });
154
+ return {
155
+ status: 0,
156
+ stdout: `${stdout || ''}`.trim(),
157
+ stderr: ''
158
+ };
159
+ } catch (error) {
160
+ return {
161
+ status: Number.isInteger(error.status) ? error.status : 1,
162
+ stdout: `${error.stdout || ''}`.trim(),
163
+ stderr: `${error.stderr || error.message || ''}`.trim()
164
+ };
165
+ }
166
+ }
167
+
168
+ const result = spawnSync('git', args, {
169
+ cwd: projectPath,
170
+ encoding: 'utf8',
171
+ windowsHide: true
172
+ });
173
+
174
+ return {
175
+ status: Number.isInteger(result.status) ? result.status : 1,
176
+ stdout: `${result.stdout || ''}`.trim(),
177
+ stderr: `${result.stderr || ''}`.trim()
178
+ };
179
+ }
180
+
181
+ function parseRemotes(raw = '', targetHosts = []) {
182
+ const lines = `${raw || ''}`.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
183
+ const byName = new Map();
184
+
185
+ for (const line of lines) {
186
+ const match = line.match(/^([^\s]+)\s+([^\s]+)\s+\((fetch|push)\)$/i);
187
+ if (!match) {
188
+ continue;
189
+ }
190
+ const name = match[1];
191
+ const url = match[2];
192
+ const kind = match[3].toLowerCase();
193
+ const current = byName.get(name) || { name, fetch: '', push: '' };
194
+ if (kind === 'fetch') {
195
+ current.fetch = url;
196
+ } else if (kind === 'push') {
197
+ current.push = url;
198
+ }
199
+ byName.set(name, current);
200
+ }
201
+
202
+ const allRemotes = Array.from(byName.values());
203
+ const normalizedHosts = targetHosts.map((item) => `${item}`.toLowerCase());
204
+ const targetRemotes = allRemotes.filter((remote) => {
205
+ const url = `${remote.fetch || remote.push || ''}`.toLowerCase();
206
+ return normalizedHosts.some((host) => url.includes(host));
207
+ });
208
+
209
+ return {
210
+ allRemotes,
211
+ targetRemotes
212
+ };
213
+ }
214
+
215
+ function parseAheadBehind(raw = '') {
216
+ const parts = `${raw}`.trim().split(/\s+/).map((item) => Number.parseInt(item, 10));
217
+ if (parts.length < 2 || !Number.isFinite(parts[0]) || !Number.isFinite(parts[1])) {
218
+ return { ahead: null, behind: null };
219
+ }
220
+ return {
221
+ ahead: parts[0],
222
+ behind: parts[1]
223
+ };
224
+ }
225
+
226
+ function parsePorcelainStatus(raw = '') {
227
+ const lines = `${raw || ''}`.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
228
+ let trackedCount = 0;
229
+ let untrackedCount = 0;
230
+ for (const line of lines) {
231
+ if (line.startsWith('??')) {
232
+ untrackedCount += 1;
233
+ continue;
234
+ }
235
+ trackedCount += 1;
236
+ }
237
+ return {
238
+ total_count: lines.length,
239
+ tracked_count: trackedCount,
240
+ untracked_count: untrackedCount
241
+ };
242
+ }
243
+
244
+ function evaluateGitManagedGate(options = {}) {
245
+ const projectPath = options.projectPath || process.cwd();
246
+ const targetHosts = Array.isArray(options.targetHosts) ? options.targetHosts : ['github.com', 'gitlab.com'];
247
+ const allowNoRemote = options.allowNoRemote !== false;
248
+ const allowUntracked = options.allowUntracked === true;
249
+ const ciContext = options.ciContext === true;
250
+ const strictCi = options.strictCi === true;
251
+ const relaxForCi = ciContext && !strictCi;
252
+
253
+ const violations = [];
254
+ const warnings = [];
255
+ const details = {
256
+ project_path: projectPath,
257
+ target_hosts: targetHosts,
258
+ ci_context: ciContext,
259
+ strict_ci: strictCi,
260
+ relaxed_ci: relaxForCi,
261
+ worktree_enforced: !relaxForCi,
262
+ remotes: [],
263
+ target_remotes: [],
264
+ branch: null,
265
+ upstream: null,
266
+ ahead: null,
267
+ behind: null,
268
+ clean_worktree: null,
269
+ worktree_changes: {
270
+ total_count: 0,
271
+ tracked_count: 0,
272
+ untracked_count: 0
273
+ }
274
+ };
275
+
276
+ const insideWorkTree = runGit(projectPath, ['rev-parse', '--is-inside-work-tree']);
277
+ if (insideWorkTree.status !== 0 || insideWorkTree.stdout.toLowerCase() !== 'true') {
278
+ return {
279
+ mode: 'git-managed-gate',
280
+ passed: false,
281
+ reason: 'not-a-git-repository',
282
+ violations: ['current directory is not a git repository'],
283
+ warnings,
284
+ details
285
+ };
286
+ }
287
+
288
+ const remotesResult = runGit(projectPath, ['remote', '-v']);
289
+ if (remotesResult.status !== 0) {
290
+ violations.push(`failed to read git remotes: ${remotesResult.stderr || 'unknown error'}`);
291
+ return {
292
+ mode: 'git-managed-gate',
293
+ passed: false,
294
+ reason: 'remote-read-failed',
295
+ violations,
296
+ warnings,
297
+ details
298
+ };
299
+ }
300
+
301
+ const remoteInfo = parseRemotes(remotesResult.stdout, targetHosts);
302
+ details.remotes = remoteInfo.allRemotes;
303
+ details.target_remotes = remoteInfo.targetRemotes;
304
+
305
+ if (remoteInfo.targetRemotes.length === 0) {
306
+ if (allowNoRemote) {
307
+ warnings.push('no GitHub/GitLab remote configured; gate bypassed by allow-no-remote policy');
308
+ return {
309
+ mode: 'git-managed-gate',
310
+ passed: true,
311
+ reason: 'no-target-remote-allowed',
312
+ violations,
313
+ warnings,
314
+ details
315
+ };
316
+ }
317
+ violations.push('no GitHub/GitLab remote configured');
318
+ return {
319
+ mode: 'git-managed-gate',
320
+ passed: false,
321
+ reason: 'no-target-remote',
322
+ violations,
323
+ warnings,
324
+ details
325
+ };
326
+ }
327
+
328
+ const statusResult = runGit(projectPath, ['status', '--porcelain']);
329
+ if (statusResult.status !== 0) {
330
+ violations.push(`failed to read git status: ${statusResult.stderr || 'unknown error'}`);
331
+ } else {
332
+ const statusSummary = parsePorcelainStatus(statusResult.stdout);
333
+ details.worktree_changes = statusSummary;
334
+ const hasTrackedChanges = statusSummary.tracked_count > 0;
335
+ const hasUntrackedFiles = statusSummary.untracked_count > 0;
336
+ details.clean_worktree = !hasTrackedChanges && !hasUntrackedFiles;
337
+
338
+ if (hasTrackedChanges) {
339
+ if (relaxForCi) {
340
+ warnings.push(`ci context detected; tracked worktree changes (${statusSummary.tracked_count}) ignored`);
341
+ } else {
342
+ violations.push('working tree has uncommitted changes');
343
+ }
344
+ }
345
+ if (hasUntrackedFiles && !allowUntracked) {
346
+ if (relaxForCi) {
347
+ warnings.push(`ci context detected; untracked files (${statusSummary.untracked_count}) ignored`);
348
+ } else {
349
+ violations.push('working tree has untracked files');
350
+ }
351
+ } else if (hasUntrackedFiles && allowUntracked) {
352
+ warnings.push(`untracked files detected (${statusSummary.untracked_count}) but allowed by policy`);
353
+ }
354
+ }
355
+
356
+ if (relaxForCi) {
357
+ warnings.push('ci context detected; branch/upstream sync checks skipped');
358
+ } else {
359
+ const branchResult = runGit(projectPath, ['rev-parse', '--abbrev-ref', 'HEAD']);
360
+ if (branchResult.status !== 0) {
361
+ violations.push(`failed to resolve branch: ${branchResult.stderr || 'unknown error'}`);
362
+ } else {
363
+ details.branch = branchResult.stdout;
364
+ if (details.branch === 'HEAD') {
365
+ violations.push('detached HEAD is not allowed for managed release');
366
+ }
367
+ }
368
+
369
+ const upstreamResult = runGit(projectPath, ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}']);
370
+ if (upstreamResult.status !== 0) {
371
+ violations.push('current branch has no upstream tracking branch');
372
+ } else {
373
+ details.upstream = upstreamResult.stdout;
374
+ const upstreamRemote = normalizeText(upstreamResult.stdout.split('/')[0]);
375
+ const upstreamIsTarget = remoteInfo.targetRemotes.some((item) => item.name === upstreamRemote);
376
+ if (!upstreamIsTarget) {
377
+ violations.push(`upstream remote "${upstreamRemote}" is not GitHub/GitLab target`);
378
+ } else {
379
+ const aheadBehindResult = runGit(projectPath, ['rev-list', '--left-right', '--count', 'HEAD...@{u}']);
380
+ if (aheadBehindResult.status !== 0) {
381
+ violations.push(`failed to compare with upstream: ${aheadBehindResult.stderr || 'unknown error'}`);
382
+ } else {
383
+ const { ahead, behind } = parseAheadBehind(aheadBehindResult.stdout);
384
+ details.ahead = ahead;
385
+ details.behind = behind;
386
+ if (!Number.isFinite(ahead) || !Number.isFinite(behind)) {
387
+ violations.push('failed to parse ahead/behind status');
388
+ } else {
389
+ if (ahead > 0) {
390
+ violations.push(`branch is ahead of upstream by ${ahead} commit(s); push required`);
391
+ }
392
+ if (behind > 0) {
393
+ violations.push(`branch is behind upstream by ${behind} commit(s); sync required`);
394
+ }
395
+ }
396
+ }
397
+ }
398
+ }
399
+ }
400
+
401
+ return {
402
+ mode: 'git-managed-gate',
403
+ passed: violations.length === 0,
404
+ reason: violations.length === 0 ? 'managed-and-synced' : 'violations',
405
+ violations,
406
+ warnings,
407
+ details
408
+ };
409
+ }
410
+
411
+ async function runGitManagedGateScript(options = {}) {
412
+ const payload = evaluateGitManagedGate(options);
413
+ if (options.json) {
414
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
415
+ } else if (payload.passed) {
416
+ process.stdout.write('[git-managed-gate] passed\n');
417
+ } else {
418
+ process.stdout.write('[git-managed-gate] blocked\n');
419
+ payload.violations.forEach((item) => {
420
+ process.stdout.write(`[git-managed-gate] violation=${item}\n`);
421
+ });
422
+ }
423
+
424
+ return {
425
+ ...payload,
426
+ exit_code: options.failOnViolation && !payload.passed ? 2 : 0
427
+ };
428
+ }
429
+
430
+ if (require.main === module) {
431
+ const options = parseArgs(process.argv.slice(2), process.env);
432
+ if (options.help) {
433
+ printHelp();
434
+ process.exit(0);
435
+ }
436
+
437
+ runGitManagedGateScript(options)
438
+ .then((result) => {
439
+ process.exitCode = result.exit_code;
440
+ })
441
+ .catch((error) => {
442
+ const payload = {
443
+ mode: 'git-managed-gate',
444
+ passed: false,
445
+ error: error.message
446
+ };
447
+ if (options.json) {
448
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
449
+ } else {
450
+ process.stderr.write(`[git-managed-gate] error=${error.message}\n`);
451
+ }
452
+ process.exitCode = 1;
453
+ });
454
+ }
455
+
456
+ module.exports = {
457
+ parseArgs,
458
+ parseRemotes,
459
+ parseAheadBehind,
460
+ parsePorcelainStatus,
461
+ runGit,
462
+ evaluateGitManagedGate,
463
+ runGitManagedGateScript
464
+ };