scene-capability-engine 3.6.38 → 3.6.44

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 (67) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/bin/scene-capability-engine.js +42 -2
  3. package/docs/command-reference.md +27 -0
  4. package/docs/developer-guide.md +1 -1
  5. package/docs/document-governance.md +22 -2
  6. package/docs/releases/README.md +6 -0
  7. package/docs/releases/v3.6.39.md +24 -0
  8. package/docs/releases/v3.6.40.md +19 -0
  9. package/docs/releases/v3.6.41.md +20 -0
  10. package/docs/releases/v3.6.42.md +19 -0
  11. package/docs/releases/v3.6.43.md +17 -0
  12. package/docs/releases/v3.6.44.md +17 -0
  13. package/docs/spec-collaboration-guide.md +1 -1
  14. package/docs/state-migration-reconciliation-runbook.md +76 -0
  15. package/docs/state-storage-tiering.md +104 -0
  16. package/docs/zh/releases/README.md +6 -0
  17. package/docs/zh/releases/v3.6.39.md +24 -0
  18. package/docs/zh/releases/v3.6.40.md +19 -0
  19. package/docs/zh/releases/v3.6.41.md +20 -0
  20. package/docs/zh/releases/v3.6.42.md +19 -0
  21. package/docs/zh/releases/v3.6.43.md +17 -0
  22. package/docs/zh/releases/v3.6.44.md +17 -0
  23. package/lib/adoption/adoption-logger.js +1 -1
  24. package/lib/adoption/adoption-strategy.js +29 -29
  25. package/lib/adoption/detection-engine.js +16 -13
  26. package/lib/adoption/smart-orchestrator.js +3 -3
  27. package/lib/adoption/strategy-selector.js +19 -15
  28. package/lib/adoption/template-sync.js +3 -3
  29. package/lib/auto/autonomous-engine.js +5 -5
  30. package/lib/auto/handoff-release-gate-history-loaders-service.js +24 -4
  31. package/lib/auto/handoff-run-service.js +37 -0
  32. package/lib/backup/backup-system.js +10 -10
  33. package/lib/collab/collab-manager.js +8 -5
  34. package/lib/collab/dependency-manager.js +1 -1
  35. package/lib/commands/adopt.js +2 -2
  36. package/lib/commands/auto.js +239 -97
  37. package/lib/commands/collab.js +10 -4
  38. package/lib/commands/docs.js +8 -2
  39. package/lib/commands/scene.js +78 -18
  40. package/lib/commands/status.js +3 -3
  41. package/lib/commands/studio.js +8 -0
  42. package/lib/commands/watch.js +10 -1
  43. package/lib/governance/config-manager.js +16 -0
  44. package/lib/governance/diagnostic-engine.js +2 -1
  45. package/lib/governance/validation-engine.js +3 -2
  46. package/lib/repo/config-manager.js +2 -2
  47. package/lib/runtime/session-store.js +8 -0
  48. package/lib/spec/bootstrap/context-collector.js +5 -4
  49. package/lib/spec-gate/rules/default-rules.js +8 -8
  50. package/lib/state/sce-state-store.js +265 -0
  51. package/lib/state/state-migration-manager.js +27 -2
  52. package/lib/state/state-storage-policy.js +179 -0
  53. package/lib/upgrade/migration-engine.js +5 -5
  54. package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +3 -3
  55. package/lib/utils/tool-detector.js +4 -4
  56. package/lib/utils/validation.js +6 -6
  57. package/lib/watch/action-executor.js +10 -1
  58. package/lib/watch/event-debouncer.js +3 -0
  59. package/lib/watch/file-watcher.js +51 -10
  60. package/lib/watch/watch-manager.js +10 -1
  61. package/lib/workspace/multi/workspace-context-resolver.js +3 -3
  62. package/lib/workspace/multi/workspace-registry.js +3 -3
  63. package/lib/workspace/multi/workspace-state-manager.js +3 -3
  64. package/lib/workspace/spec-delivery-audit.js +553 -0
  65. package/lib/workspace/takeover-baseline.js +11 -0
  66. package/package.json +5 -1
  67. package/template/.sce/config/state-storage-policy.json +165 -0
@@ -40,7 +40,7 @@ class BackupSystem {
40
40
  * @param {string} projectPath - Absolute path to project root
41
41
  * @returns {string} - Absolute path to .sce directory
42
42
  */
43
- getKiroDir(projectPath) {
43
+ getSceDir(projectPath) {
44
44
  return path.join(projectPath, '.sce');
45
45
  }
46
46
 
@@ -74,11 +74,11 @@ class BackupSystem {
74
74
  const { type = 'manual' } = options;
75
75
 
76
76
  try {
77
- const kiroDir = this.getKiroDir(projectPath);
77
+ const sceDir = this.getSceDir(projectPath);
78
78
 
79
79
  // Check if .sce/ exists
80
- const kiroExists = await pathExists(kiroDir);
81
- if (!kiroExists) {
80
+ const sceExists = await pathExists(sceDir);
81
+ if (!sceExists) {
82
82
  throw new Error('.sce/ directory does not exist');
83
83
  }
84
84
 
@@ -100,7 +100,7 @@ class BackupSystem {
100
100
  await ensureDirectory(backupPath);
101
101
 
102
102
  // Copy .sce/ contents to backup (excluding backups/ itself)
103
- const items = await listFiles(kiroDir);
103
+ const items = await listFiles(sceDir);
104
104
 
105
105
  for (const item of items) {
106
106
  // Skip the backups directory itself
@@ -108,7 +108,7 @@ class BackupSystem {
108
108
  continue;
109
109
  }
110
110
 
111
- const sourcePath = path.join(kiroDir, item);
111
+ const sourcePath = path.join(sceDir, item);
112
112
  const destPath = path.join(backupPath, item);
113
113
 
114
114
  await copyDirectory(sourcePath, destPath, { overwrite: false });
@@ -243,20 +243,20 @@ class BackupSystem {
243
243
  throw new Error(`Backup validation failed: ${backupId}`);
244
244
  }
245
245
 
246
- const kiroDir = this.getKiroDir(projectPath);
246
+ const sceDir = this.getSceDir(projectPath);
247
247
 
248
248
  // Get list of items to restore (excluding metadata.json)
249
249
  const items = await listFiles(backupPath);
250
250
  const itemsToRestore = items.filter(item => item !== 'metadata.json');
251
251
 
252
252
  // Remove existing .sce/ contents (except backups/)
253
- const existingItems = await listFiles(kiroDir);
253
+ const existingItems = await listFiles(sceDir);
254
254
  for (const item of existingItems) {
255
255
  if (item === this.backupDirName) {
256
256
  continue;
257
257
  }
258
258
 
259
- const itemPath = path.join(kiroDir, item);
259
+ const itemPath = path.join(sceDir, item);
260
260
  await remove(itemPath);
261
261
  }
262
262
 
@@ -264,7 +264,7 @@ class BackupSystem {
264
264
  const restoredFiles = [];
265
265
  for (const item of itemsToRestore) {
266
266
  const sourcePath = path.join(backupPath, item);
267
- const destPath = path.join(kiroDir, item);
267
+ const destPath = path.join(sceDir, item);
268
268
 
269
269
  await copyDirectory(sourcePath, destPath, { overwrite: true });
270
270
  restoredFiles.push(item);
@@ -125,10 +125,10 @@ class CollaborationManager {
125
125
  /**
126
126
  * Assign a spec to a SCE instance
127
127
  * @param {string} specName - Name of the spec
128
- * @param {string} kiroInstance - SCE instance identifier
128
+ * @param {string} sceInstance - SCE instance identifier
129
129
  * @returns {Promise<Object>} Assignment result
130
130
  */
131
- async assignSpec(specName, kiroInstance) {
131
+ async assignSpec(specName, sceInstance) {
132
132
  const metadata = await this.metadataManager.readMetadata(specName);
133
133
 
134
134
  if (!metadata) {
@@ -149,17 +149,20 @@ class CollaborationManager {
149
149
  // Update assignment
150
150
  const updated = await this.metadataManager.atomicUpdate(specName, (meta) => {
151
151
  meta.assignment = {
152
- kiroInstance,
152
+ sceInstance,
153
153
  assignedAt: new Date().toISOString()
154
154
  };
155
+ if (meta.assignment && Object.prototype.hasOwnProperty.call(meta.assignment, 'kiroInstance')) {
156
+ delete meta.assignment.kiroInstance;
157
+ }
155
158
  return meta;
156
159
  });
157
160
 
158
161
  return {
159
162
  success: true,
160
163
  spec: specName,
161
- kiroInstance,
162
- message: `Assigned '${specName}' to '${kiroInstance}'`
164
+ sceInstance,
165
+ message: `Assigned '${specName}' to '${sceInstance}'`
163
166
  };
164
167
  }
165
168
 
@@ -25,7 +25,7 @@ class DependencyManager {
25
25
  nodes.push({
26
26
  id: name,
27
27
  status: metadata.status?.current || 'not-started',
28
- kiroInstance: metadata.assignment?.sceInstance || null,
28
+ sceInstance: metadata.assignment?.sceInstance || metadata.assignment?.kiroInstance || null,
29
29
  type: metadata.type
30
30
  });
31
31
 
@@ -220,7 +220,7 @@ async function adoptInteractive(projectPath, options) {
220
220
  console.log(' - Preserve existing specs/ and steering/');
221
221
  console.log(' - Add missing components');
222
222
  console.log(' - Create/update version.json');
223
- if (detection.hasKiroDir) {
223
+ if (detection.hasSceDir) {
224
224
  console.log(' - Create backup before changes');
225
225
  }
226
226
  } else if (strategy === 'full') {
@@ -487,7 +487,7 @@ async function adoptInteractive(projectPath, options) {
487
487
 
488
488
  // 9. Create backup if needed (for non-conflict scenarios)
489
489
  let backupId = null;
490
- if (detection.hasKiroDir && (strategy === 'partial' || strategy === 'full')) {
490
+ if (detection.hasSceDir && (strategy === 'partial' || strategy === 'full')) {
491
491
  console.log(chalk.blue('📦 Creating backup...'));
492
492
  const backupSystem = new BackupSystem();
493
493
 
@@ -60,6 +60,7 @@ const fs = require('fs-extra');
60
60
  const path = require('path');
61
61
  const chalk = require('chalk');
62
62
  const { spawnSync } = require('child_process');
63
+ const { auditSpecDeliverySync } = require('../workspace/spec-delivery-audit');
63
64
 
64
65
  const AUTO_ARCHIVE_SCHEMA_VERSION = '1.0';
65
66
  const AUTO_ARCHIVE_SCHEMA_SUPPORTED_VERSIONS = new Set([AUTO_ARCHIVE_SCHEMA_VERSION]);
@@ -95,6 +96,7 @@ const AUTO_HANDOFF_POLICY_PROFILE_PRESETS = {
95
96
  require_capability_coverage: true,
96
97
  require_capability_semantic: true,
97
98
  require_capability_lexicon: true,
99
+ require_spec_delivery_sync: true,
98
100
  require_release_gate_preflight: true,
99
101
  dependency_batching: true,
100
102
  release_evidence_window: 5
@@ -114,6 +116,7 @@ const AUTO_HANDOFF_POLICY_PROFILE_PRESETS = {
114
116
  require_capability_coverage: true,
115
117
  require_capability_semantic: true,
116
118
  require_capability_lexicon: true,
119
+ require_spec_delivery_sync: true,
117
120
  require_release_gate_preflight: true,
118
121
  dependency_batching: true,
119
122
  release_evidence_window: 5
@@ -133,6 +136,7 @@ const AUTO_HANDOFF_POLICY_PROFILE_PRESETS = {
133
136
  require_capability_coverage: true,
134
137
  require_capability_semantic: true,
135
138
  require_capability_lexicon: true,
139
+ require_spec_delivery_sync: true,
136
140
  require_release_gate_preflight: true,
137
141
  dependency_batching: true,
138
142
  release_evidence_window: 10
@@ -1983,6 +1987,8 @@ function registerAutoCommands(program) {
1983
1987
  .description('Evaluate release-gate preflight readiness from gate-history signals')
1984
1988
  .option('--profile <profile>', 'Handoff policy profile: default|moqui|enterprise (default: default)', 'default')
1985
1989
  .option('--history-file <path>', `Release gate history file (default: ${AUTO_HANDOFF_RELEASE_GATE_HISTORY_FILE})`)
1990
+ .option('--require-spec-delivery-sync', 'Gate: require declared spec deliverables to be tracked, committed, and upstream-synced when manifests exist (default: enabled)')
1991
+ .option('--no-require-spec-delivery-sync', 'Gate: disable spec delivery sync hard requirement (not recommended)')
1986
1992
  .option('--require-release-gate-preflight', 'Gate: require release-gate preflight signal to be available and unblocked (default: enabled)')
1987
1993
  .option('--no-require-release-gate-preflight', 'Gate: disable release-gate preflight hard requirement (not recommended)')
1988
1994
  .option('--release-evidence-window <n>', 'Release evidence trend window size (2-50, default from profile)', parseInt)
@@ -1997,10 +2003,15 @@ function registerAutoCommands(program) {
1997
2003
  console.log(chalk.blue('Auto handoff preflight check:'));
1998
2004
  console.log(chalk.gray(` Status: ${result.status}`));
1999
2005
  console.log(chalk.gray(` Profile: ${result.policy.profile}`));
2006
+ console.log(chalk.gray(` Spec delivery sync: ${result.policy.require_spec_delivery_sync ? 'enabled' : 'advisory'}`));
2000
2007
  console.log(chalk.gray(` Hard-gate preflight: ${result.policy.require_release_gate_preflight ? 'enabled' : 'advisory'}`));
2001
2008
  console.log(chalk.gray(` History file: ${result.release_gate_preflight.file || 'n/a'}`));
2002
2009
  console.log(chalk.gray(` Preflight available: ${result.release_gate_preflight.available === true ? 'yes' : 'no'}`));
2003
2010
  console.log(chalk.gray(` Preflight blocked: ${result.release_gate_preflight.blocked === true ? 'yes' : 'no'}`));
2011
+ if (result.spec_delivery_sync) {
2012
+ console.log(chalk.gray(` Delivery manifests: ${result.spec_delivery_sync.summary.manifest_count}`));
2013
+ console.log(chalk.gray(` Delivery sync passed: ${result.spec_delivery_sync.passed === true ? 'yes' : 'no'}`));
2014
+ }
2004
2015
  if (result.release_gate_preflight.latest_tag) {
2005
2016
  console.log(chalk.gray(` Latest tag: ${result.release_gate_preflight.latest_tag}`));
2006
2017
  }
@@ -2088,6 +2099,8 @@ function registerAutoCommands(program) {
2088
2099
  .option('--no-require-capability-coverage', 'Gate: disable capability coverage requirement (not recommended)')
2089
2100
  .option('--require-capability-lexicon', 'Gate: require capability lexicon normalization (unknown expected/provided aliases not allowed, default: enabled)')
2090
2101
  .option('--no-require-capability-lexicon', 'Gate: disable capability lexicon normalization requirement (not recommended)')
2102
+ .option('--require-spec-delivery-sync', 'Gate: require declared spec deliverables to be tracked, committed, and upstream-synced when manifests exist (default: enabled)')
2103
+ .option('--no-require-spec-delivery-sync', 'Gate: disable spec delivery sync hard requirement (not recommended)')
2091
2104
  .option('--require-release-gate-preflight', 'Gate: require release-gate preflight signal to be available and unblocked (default: enabled)')
2092
2105
  .option('--no-require-release-gate-preflight', 'Gate: disable release-gate preflight hard requirement (not recommended)')
2093
2106
  .option('--release-evidence-window <n>', 'Release evidence trend window size (2-50, default: 5)', parseInt)
@@ -2108,6 +2121,10 @@ function registerAutoCommands(program) {
2108
2121
  if (result.template_diff) {
2109
2122
  console.log(chalk.gray(` Template compatibility: ${result.template_diff.compatibility}`));
2110
2123
  }
2124
+ if (result.spec_delivery_sync) {
2125
+ console.log(chalk.gray(` Delivery manifests: ${result.spec_delivery_sync.summary.manifest_count}`));
2126
+ console.log(chalk.gray(` Delivery sync passed: ${result.spec_delivery_sync.passed === true ? 'yes' : 'no'}`));
2127
+ }
2111
2128
  if (result.dependency_execution && Array.isArray(result.dependency_execution.batches)) {
2112
2129
  console.log(chalk.gray(` Execution batches: ${result.dependency_execution.batches.length}`));
2113
2130
  }
@@ -7326,6 +7343,10 @@ function buildAutoHandoffRunPolicy(options = {}) {
7326
7343
  options.requireCapabilityLexicon,
7327
7344
  preset.require_capability_lexicon
7328
7345
  ),
7346
+ require_spec_delivery_sync: resolveAutoHandoffPolicyOptionBoolean(
7347
+ options.requireSpecDeliverySync,
7348
+ preset.require_spec_delivery_sync
7349
+ ),
7329
7350
  require_release_gate_preflight: resolveAutoHandoffPolicyOptionBoolean(
7330
7351
  options.requireReleaseGatePreflight,
7331
7352
  preset.require_release_gate_preflight
@@ -7899,6 +7920,34 @@ function evaluateAutoHandoffReleaseGatePreflightGateReasons(policy = {}, preflig
7899
7920
  return reasons;
7900
7921
  }
7901
7922
 
7923
+ function evaluateAutoHandoffSpecDeliveryGateReasons(policy = {}, deliveryAudit = null) {
7924
+ const reasons = [];
7925
+ if (policy.require_spec_delivery_sync !== true) {
7926
+ return reasons;
7927
+ }
7928
+
7929
+ const snapshot = deliveryAudit && typeof deliveryAudit === 'object' && !Array.isArray(deliveryAudit)
7930
+ ? deliveryAudit
7931
+ : null;
7932
+ if (!snapshot) {
7933
+ reasons.push('spec delivery audit snapshot missing');
7934
+ return reasons;
7935
+ }
7936
+ if (snapshot.reason === 'no-manifests') {
7937
+ return reasons;
7938
+ }
7939
+ if (snapshot.passed !== true) {
7940
+ const violations = Array.isArray(snapshot.violations) ? snapshot.violations : [];
7941
+ if (violations.length > 0) {
7942
+ reasons.push(...violations);
7943
+ } else {
7944
+ reasons.push('spec delivery sync audit reported violations');
7945
+ }
7946
+ }
7947
+
7948
+ return reasons;
7949
+ }
7950
+
7902
7951
  function buildAutoHandoffReleaseGatePreflight(signals = null) {
7903
7952
  const source = signals && typeof signals === 'object' && !Array.isArray(signals)
7904
7953
  ? signals
@@ -7969,6 +8018,9 @@ function buildAutoHandoffPreflightCheckRecommendations(projectPath, result = {})
7969
8018
  const preflight = result && result.release_gate_preflight && typeof result.release_gate_preflight === 'object'
7970
8019
  ? result.release_gate_preflight
7971
8020
  : {};
8021
+ const deliveryAudit = result && result.spec_delivery_sync && typeof result.spec_delivery_sync === 'object'
8022
+ ? result.spec_delivery_sync
8023
+ : {};
7972
8024
  const reasons = Array.isArray(result.reasons) ? result.reasons : [];
7973
8025
  const windowSize = Number.isInteger(policy.release_evidence_window)
7974
8026
  ? policy.release_evidence_window
@@ -7984,6 +8036,16 @@ function buildAutoHandoffPreflightCheckRecommendations(projectPath, result = {})
7984
8036
  if (result.status !== 'pass' || preflight.blocked === true) {
7985
8037
  push(`sce auto handoff evidence --window ${windowSize} --json`);
7986
8038
  }
8039
+ if (
8040
+ deliveryAudit.reason === 'missing-manifest' ||
8041
+ deliveryAudit.reason === 'violations'
8042
+ ) {
8043
+ push('sce workspace delivery-audit --json --strict');
8044
+ }
8045
+ if (Number.isFinite(Number(deliveryAudit.git && deliveryAudit.git.ahead)) && Number(deliveryAudit.git.ahead) > 0) {
8046
+ const branch = normalizeHandoffText(deliveryAudit.git && deliveryAudit.git.branch) || '<branch>';
8047
+ push(`git push origin ${branch}`);
8048
+ }
7987
8049
 
7988
8050
  if (preflight.blocked === true) {
7989
8051
  const governanceRecommendations = buildGovernanceCloseLoopRecommendations(
@@ -8014,23 +8076,41 @@ function buildAutoHandoffPreflightCheckRecommendations(projectPath, result = {})
8014
8076
  'Ensure release workflow publishes `release-gate-history.json` and rerun preflight check.'
8015
8077
  );
8016
8078
  }
8079
+ if (reasons.some(item => `${item}`.includes('spec delivery'))) {
8080
+ push(
8081
+ 'Declare feature deliverables in `.sce/specs/<spec>/deliverables.json`, commit tracked files, and push upstream before handoff.'
8082
+ );
8083
+ }
8017
8084
 
8018
8085
  return recommendations;
8019
8086
  }
8020
8087
 
8021
8088
  async function buildAutoHandoffPreflightCheck(projectPath, options = {}) {
8022
8089
  const policy = buildAutoHandoffRunPolicy(options);
8090
+ const specDeliverySync = await auditSpecDeliverySync(projectPath, {
8091
+ requireManifest: false
8092
+ });
8023
8093
  const releaseGateSignals = await loadGovernanceReleaseGateSignals(projectPath, {
8024
8094
  historyFile: options.historyFile
8025
8095
  });
8026
8096
  const releaseGatePreflight = buildAutoHandoffReleaseGatePreflight(releaseGateSignals);
8027
- const hardGateReasons = evaluateAutoHandoffReleaseGatePreflightGateReasons(
8028
- policy,
8029
- releaseGatePreflight
8030
- );
8097
+ const hardGateReasons = [
8098
+ ...evaluateAutoHandoffSpecDeliveryGateReasons(policy, specDeliverySync),
8099
+ ...evaluateAutoHandoffReleaseGatePreflightGateReasons(policy, releaseGatePreflight)
8100
+ ];
8031
8101
 
8032
8102
  const advisoryReasons = [];
8033
8103
  if (hardGateReasons.length === 0) {
8104
+ if (
8105
+ policy.require_spec_delivery_sync !== true &&
8106
+ specDeliverySync.reason !== 'no-manifests' &&
8107
+ specDeliverySync.passed !== true
8108
+ ) {
8109
+ const deliveryReasons = Array.isArray(specDeliverySync.violations) && specDeliverySync.violations.length > 0
8110
+ ? specDeliverySync.violations
8111
+ : ['spec delivery sync audit reported warnings'];
8112
+ advisoryReasons.push(...deliveryReasons.map((item) => `spec delivery sync advisory: ${item}`));
8113
+ }
8034
8114
  if (releaseGatePreflight.parse_error) {
8035
8115
  advisoryReasons.push(`release gate preflight parse error: ${releaseGatePreflight.parse_error}`);
8036
8116
  } else if (releaseGatePreflight.available !== true) {
@@ -8055,9 +8135,11 @@ async function buildAutoHandoffPreflightCheck(projectPath, options = {}) {
8055
8135
  hard_gate_reasons: hardGateReasons,
8056
8136
  policy: {
8057
8137
  profile: policy.profile,
8138
+ require_spec_delivery_sync: policy.require_spec_delivery_sync === true,
8058
8139
  require_release_gate_preflight: policy.require_release_gate_preflight === true,
8059
8140
  release_evidence_window: policy.release_evidence_window
8060
8141
  },
8142
+ spec_delivery_sync: specDeliverySync,
8061
8143
  release_gate_preflight: releaseGatePreflight,
8062
8144
  signals: {
8063
8145
  history_file: releaseGateSignals.file || releaseGatePreflight.file || null,
@@ -8571,6 +8653,9 @@ function buildAutoHandoffRunRecommendations(projectPath, result) {
8571
8653
  const releaseGatePreflight = result && result.release_gate_preflight && typeof result.release_gate_preflight === 'object'
8572
8654
  ? result.release_gate_preflight
8573
8655
  : null;
8656
+ const specDeliverySync = result && result.spec_delivery_sync && typeof result.spec_delivery_sync === 'object'
8657
+ ? result.spec_delivery_sync
8658
+ : null;
8574
8659
  if (releaseGatePreflight && releaseGatePreflight.blocked === true) {
8575
8660
  push('sce auto handoff evidence --window 5 --json');
8576
8661
  if (
@@ -8596,6 +8681,17 @@ function buildAutoHandoffRunRecommendations(projectPath, result) {
8596
8681
  '--out .sce/reports/release-evidence/release-gate-history.json --json'
8597
8682
  );
8598
8683
  }
8684
+ if (
8685
+ specDeliverySync &&
8686
+ specDeliverySync.reason !== 'no-manifests' &&
8687
+ specDeliverySync.passed !== true
8688
+ ) {
8689
+ push('sce workspace delivery-audit --json --strict');
8690
+ if (Number.isFinite(Number(specDeliverySync.git && specDeliverySync.git.ahead)) && Number(specDeliverySync.git.ahead) > 0) {
8691
+ const branch = normalizeHandoffText(specDeliverySync.git && specDeliverySync.git.branch) || '<branch>';
8692
+ push(`git push origin ${branch}`);
8693
+ }
8694
+ }
8599
8695
 
8600
8696
  const riskLevel = result && result.gates && result.gates.actual && typeof result.gates.actual.risk_level === 'string'
8601
8697
  ? result.gates.actual.risk_level.trim().toLowerCase()
@@ -9888,6 +9984,8 @@ async function runAutoHandoff(projectPath, options = {}) {
9888
9984
  loadGovernanceReleaseGateSignals,
9889
9985
  completeAutoHandoffRunPhase,
9890
9986
  evaluateAutoHandoffOntologyGateReasons,
9987
+ auditSpecDeliverySync,
9988
+ evaluateAutoHandoffSpecDeliveryGateReasons,
9891
9989
  evaluateAutoHandoffReleaseGatePreflightGateReasons,
9892
9990
  failAutoHandoffRunPhase,
9893
9991
  buildAutoHandoffMoquiBaselineSnapshot,
@@ -11717,6 +11815,130 @@ async function loadGovernanceReleaseGateSignals(projectPath, options = {}) {
11717
11815
  const parsed = Number(value);
11718
11816
  return Number.isFinite(parsed) ? parsed : null;
11719
11817
  };
11818
+ const buildSignalsSnapshot = ({ file, totalEntries, latest, aggregates, parseError = null }) => {
11819
+ const latestRiskLevel = normalizeHandoffText(latest && latest.risk_level);
11820
+ const latestWeeklyOpsRiskLevel = normalizeHandoffText(latest && latest.weekly_ops_risk_level);
11821
+ const normalizedTotalEntries = Number.isFinite(Number(totalEntries))
11822
+ ? Number(totalEntries)
11823
+ : 0;
11824
+ const hasEntries = normalizedTotalEntries > 0 || Boolean(latest);
11825
+ return {
11826
+ ...base,
11827
+ available: hasEntries,
11828
+ file: normalizeHandoffText(file) || base.file,
11829
+ total_entries: normalizedTotalEntries,
11830
+ latest_tag: normalizeHandoffText(latest && latest.tag) || null,
11831
+ latest_gate_passed: parseAutoHandoffGateBoolean(latest && latest.gate_passed, null),
11832
+ latest_risk_level: latestRiskLevel
11833
+ ? normalizeAutoHandoffGateRiskLevel(latestRiskLevel)
11834
+ : null,
11835
+ pass_rate_percent: toNumber(aggregates && aggregates.pass_rate_percent),
11836
+ scene_package_batch_pass_rate_percent: toNumber(
11837
+ aggregates && aggregates.scene_package_batch_pass_rate_percent
11838
+ ),
11839
+ scene_package_batch_failed_count: toNumber(
11840
+ aggregates && aggregates.scene_package_batch_failed_count
11841
+ ),
11842
+ drift_alert_rate_percent: toNumber(aggregates && aggregates.drift_alert_rate_percent),
11843
+ drift_alert_runs: toNumber(aggregates && aggregates.drift_alert_runs),
11844
+ drift_blocked_runs: toNumber(aggregates && aggregates.drift_blocked_runs),
11845
+ latest_weekly_ops_blocked: parseAutoHandoffGateBoolean(latest && latest.weekly_ops_blocked, null),
11846
+ latest_weekly_ops_risk_level: latestWeeklyOpsRiskLevel
11847
+ ? normalizeAutoHandoffGateRiskLevel(latestWeeklyOpsRiskLevel)
11848
+ : null,
11849
+ latest_weekly_ops_governance_status: normalizeHandoffText(
11850
+ latest && latest.weekly_ops_governance_status
11851
+ ) || null,
11852
+ latest_weekly_ops_authorization_tier_block_rate_percent: toNumber(
11853
+ latest && latest.weekly_ops_authorization_tier_block_rate_percent
11854
+ ),
11855
+ latest_weekly_ops_dialogue_authorization_block_rate_percent: toNumber(
11856
+ latest && latest.weekly_ops_dialogue_authorization_block_rate_percent
11857
+ ),
11858
+ latest_weekly_ops_config_warning_count: toNumber(
11859
+ latest && latest.weekly_ops_config_warning_count
11860
+ ),
11861
+ latest_weekly_ops_runtime_block_rate_percent: toNumber(
11862
+ latest && latest.weekly_ops_runtime_block_rate_percent
11863
+ ),
11864
+ latest_weekly_ops_runtime_ui_mode_violation_total: toNumber(
11865
+ latest && latest.weekly_ops_runtime_ui_mode_violation_total
11866
+ ),
11867
+ latest_weekly_ops_runtime_ui_mode_violation_rate_percent: toNumber(
11868
+ latest && latest.weekly_ops_runtime_ui_mode_violation_rate_percent
11869
+ ),
11870
+ weekly_ops_known_runs: toNumber(aggregates && aggregates.weekly_ops_known_runs),
11871
+ weekly_ops_blocked_runs: toNumber(aggregates && aggregates.weekly_ops_blocked_runs),
11872
+ weekly_ops_block_rate_percent: toNumber(aggregates && aggregates.weekly_ops_block_rate_percent),
11873
+ weekly_ops_violations_total: toNumber(aggregates && aggregates.weekly_ops_violations_total),
11874
+ weekly_ops_warnings_total: toNumber(aggregates && aggregates.weekly_ops_warnings_total),
11875
+ weekly_ops_config_warnings_total: toNumber(
11876
+ aggregates && aggregates.weekly_ops_config_warnings_total
11877
+ ),
11878
+ weekly_ops_authorization_tier_block_rate_max_percent: toNumber(
11879
+ aggregates && aggregates.weekly_ops_authorization_tier_block_rate_max_percent
11880
+ ),
11881
+ weekly_ops_dialogue_authorization_block_rate_max_percent: toNumber(
11882
+ aggregates && aggregates.weekly_ops_dialogue_authorization_block_rate_max_percent
11883
+ ),
11884
+ weekly_ops_runtime_block_rate_avg_percent: toNumber(
11885
+ aggregates && aggregates.weekly_ops_runtime_block_rate_avg_percent
11886
+ ),
11887
+ weekly_ops_runtime_block_rate_max_percent: toNumber(
11888
+ aggregates && aggregates.weekly_ops_runtime_block_rate_max_percent
11889
+ ),
11890
+ weekly_ops_runtime_ui_mode_violation_known_runs: toNumber(
11891
+ aggregates && aggregates.weekly_ops_runtime_ui_mode_violation_known_runs
11892
+ ),
11893
+ weekly_ops_runtime_ui_mode_violation_runs: toNumber(
11894
+ aggregates && aggregates.weekly_ops_runtime_ui_mode_violation_runs
11895
+ ),
11896
+ weekly_ops_runtime_ui_mode_violation_run_rate_percent: toNumber(
11897
+ aggregates && aggregates.weekly_ops_runtime_ui_mode_violation_run_rate_percent
11898
+ ),
11899
+ weekly_ops_runtime_ui_mode_violation_total: toNumber(
11900
+ aggregates && aggregates.weekly_ops_runtime_ui_mode_violation_total
11901
+ ),
11902
+ weekly_ops_runtime_ui_mode_violation_rate_avg_percent: toNumber(
11903
+ aggregates && aggregates.weekly_ops_runtime_ui_mode_violation_rate_avg_percent
11904
+ ),
11905
+ weekly_ops_runtime_ui_mode_violation_rate_max_percent: toNumber(
11906
+ aggregates && aggregates.weekly_ops_runtime_ui_mode_violation_rate_max_percent
11907
+ ),
11908
+ parse_error: parseError
11909
+ };
11910
+ };
11911
+ const loadFallbackSnapshot = async (parseError = null) => {
11912
+ const reportDir = path.dirname(historyFile);
11913
+ const reportResult = await loadAutoHandoffReleaseGateReports(projectPath, reportDir);
11914
+ const mergedEntries = mergeAutoHandoffReleaseGateHistoryEntries(reportResult.entries);
11915
+ if (mergedEntries.length === 0) {
11916
+ return parseError
11917
+ ? {
11918
+ ...base,
11919
+ parse_error: parseError
11920
+ }
11921
+ : base;
11922
+ }
11923
+ mergedEntries.sort((left, right) => {
11924
+ const leftTs = toAutoHandoffTimestamp(left && left.evaluated_at);
11925
+ const rightTs = toAutoHandoffTimestamp(right && right.evaluated_at);
11926
+ if (rightTs !== leftTs) {
11927
+ return rightTs - leftTs;
11928
+ }
11929
+ const leftTag = normalizeHandoffText(left && left.tag) || '';
11930
+ const rightTag = normalizeHandoffText(right && right.tag) || '';
11931
+ return rightTag.localeCompare(leftTag);
11932
+ });
11933
+ const latest = mergedEntries[0] || null;
11934
+ return buildSignalsSnapshot({
11935
+ file: normalizeHandoffText(latest && latest.file) || reportDir,
11936
+ totalEntries: mergedEntries.length,
11937
+ latest,
11938
+ aggregates: buildAutoHandoffReleaseGateHistoryAggregates(mergedEntries),
11939
+ parseError: null
11940
+ });
11941
+ };
11720
11942
  const base = {
11721
11943
  available: false,
11722
11944
  file: historyFile,
@@ -11758,22 +11980,16 @@ async function loadGovernanceReleaseGateSignals(projectPath, options = {}) {
11758
11980
  parse_error: null
11759
11981
  };
11760
11982
  if (!(await fs.pathExists(historyFile))) {
11761
- return base;
11983
+ return loadFallbackSnapshot();
11762
11984
  }
11763
11985
  let payload = null;
11764
11986
  try {
11765
11987
  payload = await fs.readJson(historyFile);
11766
11988
  } catch (error) {
11767
- return {
11768
- ...base,
11769
- parse_error: `${error.message}`
11770
- };
11989
+ return loadFallbackSnapshot(`${error.message}`);
11771
11990
  }
11772
11991
  if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
11773
- return {
11774
- ...base,
11775
- parse_error: 'invalid release gate history payload'
11776
- };
11992
+ return loadFallbackSnapshot('invalid release gate history payload');
11777
11993
  }
11778
11994
  const entries = Array.isArray(payload.entries) ? payload.entries : [];
11779
11995
  const latest = payload.latest && typeof payload.latest === 'object'
@@ -11782,86 +11998,16 @@ async function loadGovernanceReleaseGateSignals(projectPath, options = {}) {
11782
11998
  const aggregates = payload.aggregates && typeof payload.aggregates === 'object'
11783
11999
  ? payload.aggregates
11784
12000
  : {};
11785
- const latestRiskLevel = normalizeHandoffText(latest && latest.risk_level);
11786
- const latestWeeklyOpsRiskLevel = normalizeHandoffText(latest && latest.weekly_ops_risk_level);
11787
- return {
11788
- ...base,
11789
- available: true,
11790
- total_entries: toNumber(payload.total_entries) || entries.length,
11791
- latest_tag: normalizeHandoffText(latest && latest.tag) || null,
11792
- latest_gate_passed: parseAutoHandoffGateBoolean(latest && latest.gate_passed, null),
11793
- latest_risk_level: latestRiskLevel
11794
- ? normalizeAutoHandoffGateRiskLevel(latestRiskLevel)
11795
- : null,
11796
- pass_rate_percent: toNumber(aggregates.pass_rate_percent),
11797
- scene_package_batch_pass_rate_percent: toNumber(aggregates.scene_package_batch_pass_rate_percent),
11798
- scene_package_batch_failed_count: toNumber(aggregates.scene_package_batch_failed_count),
11799
- drift_alert_rate_percent: toNumber(aggregates.drift_alert_rate_percent),
11800
- drift_alert_runs: toNumber(aggregates.drift_alert_runs),
11801
- drift_blocked_runs: toNumber(aggregates.drift_blocked_runs),
11802
- latest_weekly_ops_blocked: parseAutoHandoffGateBoolean(latest && latest.weekly_ops_blocked, null),
11803
- latest_weekly_ops_risk_level: latestWeeklyOpsRiskLevel
11804
- ? normalizeAutoHandoffGateRiskLevel(latestWeeklyOpsRiskLevel)
11805
- : null,
11806
- latest_weekly_ops_governance_status: normalizeHandoffText(
11807
- latest && latest.weekly_ops_governance_status
11808
- ) || null,
11809
- latest_weekly_ops_authorization_tier_block_rate_percent: toNumber(
11810
- latest && latest.weekly_ops_authorization_tier_block_rate_percent
11811
- ),
11812
- latest_weekly_ops_dialogue_authorization_block_rate_percent: toNumber(
11813
- latest && latest.weekly_ops_dialogue_authorization_block_rate_percent
11814
- ),
11815
- latest_weekly_ops_config_warning_count: toNumber(
11816
- latest && latest.weekly_ops_config_warning_count
11817
- ),
11818
- latest_weekly_ops_runtime_block_rate_percent: toNumber(
11819
- latest && latest.weekly_ops_runtime_block_rate_percent
11820
- ),
11821
- latest_weekly_ops_runtime_ui_mode_violation_total: toNumber(
11822
- latest && latest.weekly_ops_runtime_ui_mode_violation_total
11823
- ),
11824
- latest_weekly_ops_runtime_ui_mode_violation_rate_percent: toNumber(
11825
- latest && latest.weekly_ops_runtime_ui_mode_violation_rate_percent
11826
- ),
11827
- weekly_ops_known_runs: toNumber(aggregates.weekly_ops_known_runs),
11828
- weekly_ops_blocked_runs: toNumber(aggregates.weekly_ops_blocked_runs),
11829
- weekly_ops_block_rate_percent: toNumber(aggregates.weekly_ops_block_rate_percent),
11830
- weekly_ops_violations_total: toNumber(aggregates.weekly_ops_violations_total),
11831
- weekly_ops_warnings_total: toNumber(aggregates.weekly_ops_warnings_total),
11832
- weekly_ops_config_warnings_total: toNumber(aggregates.weekly_ops_config_warnings_total),
11833
- weekly_ops_authorization_tier_block_rate_max_percent: toNumber(
11834
- aggregates.weekly_ops_authorization_tier_block_rate_max_percent
11835
- ),
11836
- weekly_ops_dialogue_authorization_block_rate_max_percent: toNumber(
11837
- aggregates.weekly_ops_dialogue_authorization_block_rate_max_percent
11838
- ),
11839
- weekly_ops_runtime_block_rate_avg_percent: toNumber(
11840
- aggregates.weekly_ops_runtime_block_rate_avg_percent
11841
- ),
11842
- weekly_ops_runtime_block_rate_max_percent: toNumber(
11843
- aggregates.weekly_ops_runtime_block_rate_max_percent
11844
- ),
11845
- weekly_ops_runtime_ui_mode_violation_known_runs: toNumber(
11846
- aggregates.weekly_ops_runtime_ui_mode_violation_known_runs
11847
- ),
11848
- weekly_ops_runtime_ui_mode_violation_runs: toNumber(
11849
- aggregates.weekly_ops_runtime_ui_mode_violation_runs
11850
- ),
11851
- weekly_ops_runtime_ui_mode_violation_run_rate_percent: toNumber(
11852
- aggregates.weekly_ops_runtime_ui_mode_violation_run_rate_percent
11853
- ),
11854
- weekly_ops_runtime_ui_mode_violation_total: toNumber(
11855
- aggregates.weekly_ops_runtime_ui_mode_violation_total
11856
- ),
11857
- weekly_ops_runtime_ui_mode_violation_rate_avg_percent: toNumber(
11858
- aggregates.weekly_ops_runtime_ui_mode_violation_rate_avg_percent
11859
- ),
11860
- weekly_ops_runtime_ui_mode_violation_rate_max_percent: toNumber(
11861
- aggregates.weekly_ops_runtime_ui_mode_violation_rate_max_percent
11862
- ),
11863
- parse_error: null
11864
- };
12001
+ if (entries.length === 0 && !latest) {
12002
+ return loadFallbackSnapshot();
12003
+ }
12004
+ return buildSignalsSnapshot({
12005
+ file: historyFile,
12006
+ totalEntries: toNumber(payload.total_entries) || entries.length,
12007
+ latest,
12008
+ aggregates,
12009
+ parseError: null
12010
+ });
11865
12011
  }
11866
12012
 
11867
12013
  async function loadGovernanceHandoffQualitySignals(projectPath) {
@@ -12799,9 +12945,6 @@ function evaluateGovernanceReleaseGateBlockState(assessment) {
12799
12945
  ) {
12800
12946
  reasons.push(`scene-batch-pass-rate-low:${snapshot.scene_package_batch_pass_rate_percent}`);
12801
12947
  }
12802
- if (Number.isFinite(snapshot.drift_alert_rate_percent) && snapshot.drift_alert_rate_percent > 0) {
12803
- reasons.push(`drift-alert-rate-positive:${snapshot.drift_alert_rate_percent}`);
12804
- }
12805
12948
  if (Number.isFinite(snapshot.drift_blocked_runs) && snapshot.drift_blocked_runs > 0) {
12806
12949
  reasons.push(`drift-blocked-runs-positive:${snapshot.drift_blocked_runs}`);
12807
12950
  }
@@ -12995,7 +13138,6 @@ function evaluateGovernanceReleaseGateBlockState(assessment) {
12995
13138
  snapshot.available === true &&
12996
13139
  (
12997
13140
  snapshot.latest_gate_passed === false ||
12998
- (Number.isFinite(snapshot.drift_alert_rate_percent) && snapshot.drift_alert_rate_percent > 0) ||
12999
13141
  (Number.isFinite(snapshot.drift_blocked_runs) && snapshot.drift_blocked_runs > 0)
13000
13142
  )
13001
13143
  );