scene-capability-engine 3.4.5 → 3.4.6

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.
@@ -19,6 +19,8 @@ const STUDIO_EVENT_API_VERSION = 'sce.studio.event/v0.1';
19
19
  const VERIFY_PROFILES = new Set(['fast', 'standard', 'strict']);
20
20
  const RELEASE_PROFILES = new Set(['standard', 'strict']);
21
21
  const STUDIO_REPORTS_DIR = '.sce/reports/studio';
22
+ const DEFAULT_INTERACTIVE_GOVERNANCE_REPORT = '.sce/reports/interactive-governance-report.json';
23
+ const DEFAULT_PROBLEM_CONTRACT_RELATIVE_PATH = path.join('custom', 'problem-contract.json');
22
24
  const MAX_OUTPUT_PREVIEW_LENGTH = 2000;
23
25
  const DEFAULT_STUDIO_SECURITY_POLICY = Object.freeze({
24
26
  enabled: false,
@@ -368,6 +370,7 @@ async function buildVerifyGateSteps(options = {}, dependencies = {}) {
368
370
  const projectPath = dependencies.projectPath || process.cwd();
369
371
  const fileSystem = dependencies.fileSystem || fs;
370
372
  const profile = normalizeString(options.profile) || 'standard';
373
+ const specId = normalizeString(options.specId || dependencies.specId);
371
374
 
372
375
  if (!VERIFY_PROFILES.has(profile)) {
373
376
  throw new Error(`Invalid verify profile "${profile}". Expected one of: ${Array.from(VERIFY_PROFILES).join(', ')}`);
@@ -403,6 +406,21 @@ async function buildVerifyGateSteps(options = {}, dependencies = {}) {
403
406
  }
404
407
 
405
408
  if (profile === 'standard' || profile === 'strict') {
409
+ const problemClosureGateScript = path.join(projectPath, 'scripts', 'problem-closure-gate.js');
410
+ const hasProblemClosureGateScript = await fileSystem.pathExists(problemClosureGateScript);
411
+ const canRunProblemClosureGate = hasProblemClosureGateScript && Boolean(specId);
412
+ steps.push({
413
+ id: 'problem-closure-gate',
414
+ name: 'problem closure gate (verify)',
415
+ command: 'node',
416
+ args: ['scripts/problem-closure-gate.js', '--stage', 'verify', '--spec', specId, '--fail-on-block', '--json'],
417
+ required: Boolean(specId),
418
+ enabled: canRunProblemClosureGate,
419
+ skip_reason: canRunProblemClosureGate
420
+ ? ''
421
+ : (specId ? 'scripts/problem-closure-gate.js not found' : 'spec id unavailable for problem-closure gate')
422
+ });
423
+
406
424
  const governanceScript = path.join(projectPath, 'scripts', 'interactive-governance-report.js');
407
425
  const hasGovernanceScript = await fileSystem.pathExists(governanceScript);
408
426
  steps.push({
@@ -435,11 +453,32 @@ async function buildReleaseGateSteps(options = {}, dependencies = {}) {
435
453
  const projectPath = dependencies.projectPath || process.cwd();
436
454
  const fileSystem = dependencies.fileSystem || fs;
437
455
  const profile = normalizeString(options.profile) || 'standard';
456
+ const specId = normalizeString(options.specId || dependencies.specId);
457
+ const verifyReportPath = normalizeString(options.verifyReportPath || dependencies.verifyReportPath);
438
458
  if (!RELEASE_PROFILES.has(profile)) {
439
459
  throw new Error(`Invalid release profile "${profile}". Expected one of: ${Array.from(RELEASE_PROFILES).join(', ')}`);
440
460
  }
441
461
 
442
462
  const steps = [];
463
+ const problemClosureGateScript = path.join(projectPath, 'scripts', 'problem-closure-gate.js');
464
+ const hasProblemClosureGateScript = await fileSystem.pathExists(problemClosureGateScript);
465
+ const canRunProblemClosureGate = hasProblemClosureGateScript && Boolean(specId);
466
+ const problemClosureArgs = ['scripts/problem-closure-gate.js', '--stage', 'release', '--spec', specId, '--fail-on-block', '--json'];
467
+ if (verifyReportPath) {
468
+ problemClosureArgs.push('--verify-report', verifyReportPath);
469
+ }
470
+ steps.push({
471
+ id: 'problem-closure-gate',
472
+ name: 'problem closure gate (release)',
473
+ command: 'node',
474
+ args: problemClosureArgs,
475
+ required: Boolean(specId),
476
+ enabled: canRunProblemClosureGate,
477
+ skip_reason: canRunProblemClosureGate
478
+ ? ''
479
+ : (specId ? 'scripts/problem-closure-gate.js not found' : 'spec id unavailable for problem-closure gate')
480
+ });
481
+
443
482
  steps.push({
444
483
  id: 'npm-pack-dry-run',
445
484
  name: 'npm pack --dry-run',
@@ -710,6 +749,77 @@ async function readSpecDomainChain(projectPath, specId, fileSystem = fs) {
710
749
  }
711
750
  }
712
751
 
752
+ async function readSpecProblemContract(projectPath, specId, fileSystem = fs) {
753
+ const specRoot = path.join(projectPath, '.sce', 'specs', specId);
754
+ const contractPath = path.join(specRoot, DEFAULT_PROBLEM_CONTRACT_RELATIVE_PATH);
755
+ if (!await fileSystem.pathExists(contractPath)) {
756
+ return null;
757
+ }
758
+ try {
759
+ const payload = await fileSystem.readJson(contractPath);
760
+ const stat = await fileSystem.stat(contractPath);
761
+ return {
762
+ spec_id: specId,
763
+ contract_path: toRelativePosix(projectPath, contractPath),
764
+ payload,
765
+ updated_at: stat && stat.mtime ? stat.mtime.toISOString() : null,
766
+ mtime_ms: Number(stat && stat.mtimeMs) || 0
767
+ };
768
+ } catch (_error) {
769
+ return null;
770
+ }
771
+ }
772
+
773
+ async function readGovernanceSignals(projectPath, fileSystem = fs) {
774
+ const reportPath = path.join(projectPath, DEFAULT_INTERACTIVE_GOVERNANCE_REPORT);
775
+ if (!await fileSystem.pathExists(reportPath)) {
776
+ return {
777
+ available: false,
778
+ report_path: null,
779
+ high_breach_count: 0,
780
+ medium_breach_count: 0
781
+ };
782
+ }
783
+ const payload = await fileSystem.readJson(reportPath).catch(() => null);
784
+ const summary = extractGovernanceBreachSignals(payload || {});
785
+ return {
786
+ ...summary,
787
+ report_path: toRelativePosix(projectPath, reportPath)
788
+ };
789
+ }
790
+
791
+ async function readVerifyReportSignals(projectPath, verifyReportPath = '', fileSystem = fs) {
792
+ const normalized = normalizeString(verifyReportPath);
793
+ if (!normalized) {
794
+ return {
795
+ available: false,
796
+ report_path: null,
797
+ passed: false,
798
+ failed_step_count: 0
799
+ };
800
+ }
801
+ const absolutePath = path.isAbsolute(normalized)
802
+ ? normalized
803
+ : path.join(projectPath, normalized);
804
+ if (!await fileSystem.pathExists(absolutePath)) {
805
+ return {
806
+ available: false,
807
+ report_path: normalized,
808
+ passed: false,
809
+ failed_step_count: 0
810
+ };
811
+ }
812
+ const payload = await fileSystem.readJson(absolutePath).catch(() => null);
813
+ const steps = Array.isArray(payload && payload.steps) ? payload.steps : [];
814
+ const failedStepCount = steps.filter((step) => normalizeString(step && step.status) === 'failed').length;
815
+ return {
816
+ available: true,
817
+ report_path: toRelativePosix(projectPath, absolutePath),
818
+ passed: payload && payload.passed === true && failedStepCount === 0,
819
+ failed_step_count: failedStepCount
820
+ };
821
+ }
822
+
713
823
  function normalizeChainList(value, limit = 5) {
714
824
  if (!Array.isArray(value)) {
715
825
  return [];
@@ -725,13 +835,97 @@ function normalizeChainList(value, limit = 5) {
725
835
  });
726
836
  }
727
837
 
838
+ function normalizeProblemContract(contract = {}, context = {}) {
839
+ const source = contract && typeof contract === 'object' ? contract : {};
840
+ const issueStatement = normalizeString(
841
+ source.issue_statement
842
+ || source.issue
843
+ || source.problem_statement
844
+ || context.problem_statement
845
+ || context.goal
846
+ );
847
+ const expectedOutcome = normalizeString(
848
+ source.expected_outcome
849
+ || source.expected
850
+ || source.success_criteria
851
+ || context.verification_plan
852
+ || (context.scene_id ? `Scene ${context.scene_id} reaches deterministic verification gates.` : '')
853
+ );
854
+ const reproductionSteps = normalizeChainList(
855
+ source.reproduction_steps || source.repro_steps || source.steps,
856
+ 20
857
+ );
858
+ const fallbackRepro = reproductionSteps.length > 0
859
+ ? reproductionSteps
860
+ : [
861
+ normalizeString(context.goal) || 'Reproduce the reported issue in the target scene.',
862
+ 'Capture logs and gate evidence for the failing path.'
863
+ ].filter(Boolean);
864
+ const forbiddenWorkarounds = normalizeChainList(
865
+ source.forbidden_workarounds || source.prohibited_workarounds || source.disallowed_workarounds,
866
+ 20
867
+ );
868
+ const fallbackForbidden = forbiddenWorkarounds.length > 0
869
+ ? forbiddenWorkarounds
870
+ : [
871
+ 'Do not bypass gates or tests.',
872
+ 'Do not silence runtime errors.'
873
+ ];
874
+
875
+ return {
876
+ issue_statement: issueStatement,
877
+ expected_outcome: expectedOutcome,
878
+ reproduction_steps: fallbackRepro,
879
+ impact_scope: normalizeString(source.impact_scope || source.scope || context.scene_id),
880
+ forbidden_workarounds: fallbackForbidden
881
+ };
882
+ }
883
+
884
+ function extractGovernanceBreachSignals(report = {}) {
885
+ if (!report || typeof report !== 'object') {
886
+ return {
887
+ available: false,
888
+ high_breach_count: 0,
889
+ medium_breach_count: 0
890
+ };
891
+ }
892
+ const alerts = Array.isArray(report.alerts) ? report.alerts : [];
893
+ let highBreachCount = 0;
894
+ let mediumBreachCount = 0;
895
+ for (const alert of alerts) {
896
+ const status = normalizeString(alert && alert.status).toLowerCase();
897
+ const severity = normalizeString(alert && alert.severity).toLowerCase();
898
+ if (status !== 'breach') {
899
+ continue;
900
+ }
901
+ if (severity === 'high') {
902
+ highBreachCount += 1;
903
+ } else if (severity === 'medium') {
904
+ mediumBreachCount += 1;
905
+ }
906
+ }
907
+ return {
908
+ available: true,
909
+ high_breach_count: highBreachCount,
910
+ medium_breach_count: mediumBreachCount
911
+ };
912
+ }
913
+
728
914
  function summarizeDomainChain(payload = {}) {
729
915
  const ontology = payload && typeof payload.ontology === 'object' ? payload.ontology : {};
916
+ const ontologyEvidence = payload && typeof payload.ontology_evidence === 'object' ? payload.ontology_evidence : {};
730
917
  const decisionPath = Array.isArray(payload.decision_execution_path) ? payload.decision_execution_path : [];
731
918
  const correctionLoop = payload && typeof payload.correction_loop === 'object' ? payload.correction_loop : {};
732
919
  const verification = payload && typeof payload.verification === 'object' ? payload.verification : {};
733
920
  const hypotheses = Array.isArray(payload.hypotheses) ? payload.hypotheses : [];
734
921
  const risks = Array.isArray(payload.risks) ? payload.risks : [];
922
+ const evidenceBindingCount = (
923
+ normalizeChainList(ontologyEvidence.entity, 50).length
924
+ + normalizeChainList(ontologyEvidence.relation, 50).length
925
+ + normalizeChainList(ontologyEvidence.business_rule, 50).length
926
+ + normalizeChainList(ontologyEvidence.decision_policy, 50).length
927
+ + normalizeChainList(ontologyEvidence.execution_flow, 50).length
928
+ );
735
929
 
736
930
  return {
737
931
  scene_id: normalizeString(payload.scene_id) || null,
@@ -747,6 +941,8 @@ function summarizeDomainChain(payload = {}) {
747
941
  hypothesis_count: hypotheses.length,
748
942
  risk_count: risks.length,
749
943
  decision_path_steps: decisionPath.length,
944
+ evidence_binding_count: evidenceBindingCount,
945
+ verification_plan: normalizeString(verification.plan) || null,
750
946
  correction_loop: {
751
947
  triggers: normalizeChainList(correctionLoop.triggers, 5),
752
948
  actions: normalizeChainList(correctionLoop.actions, 5)
@@ -781,7 +977,13 @@ function buildDomainChainRuntimeContext(payload = {}) {
781
977
  verification: {
782
978
  plan: normalizeString(payload?.verification?.plan) || null,
783
979
  gates: normalizeChainList(payload?.verification?.gates, 10)
784
- }
980
+ },
981
+ problem_contract: normalizeProblemContract(payload?.problem_contract || {}, {
982
+ scene_id: normalizeString(payload.scene_id) || '',
983
+ goal: normalizeString(payload?.problem?.statement) || '',
984
+ problem_statement: normalizeString(payload?.problem?.statement) || '',
985
+ verification_plan: normalizeString(payload?.verification?.plan) || ''
986
+ })
785
987
  };
786
988
  }
787
989
 
@@ -834,12 +1036,22 @@ async function resolveDomainChainBinding(options = {}, dependencies = {}) {
834
1036
  problemStatement: goal || `Studio scene cycle for ${sceneId}`
835
1037
  });
836
1038
  const chain = await readSpecDomainChain(projectPath, explicitSpec, fileSystem);
1039
+ const problemContract = await readSpecProblemContract(projectPath, explicitSpec, fileSystem);
837
1040
  if (!chain) {
838
1041
  return {
839
1042
  resolved: false,
840
1043
  source: 'explicit-spec',
841
1044
  spec_id: explicitSpec,
842
- reason: 'domain_chain_missing'
1045
+ reason: 'domain_chain_missing',
1046
+ problem_contract: problemContract
1047
+ ? normalizeProblemContract(problemContract.payload, {
1048
+ scene_id: sceneId,
1049
+ goal
1050
+ })
1051
+ : normalizeProblemContract({}, {
1052
+ scene_id: sceneId,
1053
+ goal
1054
+ })
843
1055
  };
844
1056
  }
845
1057
  return {
@@ -849,7 +1061,17 @@ async function resolveDomainChainBinding(options = {}, dependencies = {}) {
849
1061
  chain_path: chain.chain_path,
850
1062
  updated_at: chain.updated_at,
851
1063
  summary: summarizeDomainChain(chain.payload),
852
- context: buildDomainChainRuntimeContext(chain.payload)
1064
+ context: buildDomainChainRuntimeContext(chain.payload),
1065
+ problem_contract: normalizeProblemContract(
1066
+ problemContract && problemContract.payload ? problemContract.payload : chain.payload?.problem_contract || {},
1067
+ {
1068
+ scene_id: sceneId,
1069
+ goal,
1070
+ problem_statement: normalizeString(chain?.payload?.problem?.statement),
1071
+ verification_plan: normalizeString(chain?.payload?.verification?.plan)
1072
+ }
1073
+ ),
1074
+ problem_contract_path: problemContract ? problemContract.contract_path : null
853
1075
  };
854
1076
  }
855
1077
 
@@ -873,6 +1095,7 @@ async function resolveDomainChainBinding(options = {}, dependencies = {}) {
873
1095
  }
874
1096
 
875
1097
  const selected = candidates[0];
1098
+ const selectedContract = await readSpecProblemContract(projectPath, selected.spec_id, fileSystem);
876
1099
  return {
877
1100
  resolved: true,
878
1101
  source: candidates.length === 1 ? 'scene-auto-single' : 'scene-auto-latest',
@@ -886,7 +1109,17 @@ async function resolveDomainChainBinding(options = {}, dependencies = {}) {
886
1109
  updated_at: item.updated_at
887
1110
  })),
888
1111
  summary: summarizeDomainChain(selected.payload),
889
- context: buildDomainChainRuntimeContext(selected.payload)
1112
+ context: buildDomainChainRuntimeContext(selected.payload),
1113
+ problem_contract: normalizeProblemContract(
1114
+ selectedContract && selectedContract.payload ? selectedContract.payload : selected.payload?.problem_contract || {},
1115
+ {
1116
+ scene_id: sceneId,
1117
+ goal,
1118
+ problem_statement: normalizeString(selected?.payload?.problem?.statement),
1119
+ verification_plan: normalizeString(selected?.payload?.verification?.plan)
1120
+ }
1121
+ ),
1122
+ problem_contract_path: selectedContract ? selectedContract.contract_path : null
890
1123
  };
891
1124
  }
892
1125
 
@@ -949,6 +1182,14 @@ function buildJobDomainChainMetadata(job = {}) {
949
1182
  : null;
950
1183
  const summary = domainChain && domainChain.summary ? domainChain.summary : null;
951
1184
  const context = domainChain && domainChain.context ? domainChain.context : null;
1185
+ const problemContract = job?.source?.problem_contract && typeof job.source.problem_contract === 'object'
1186
+ ? job.source.problem_contract
1187
+ : normalizeProblemContract(context && context.problem_contract ? context.problem_contract : {}, {
1188
+ scene_id: normalizeString(job?.scene?.id),
1189
+ goal: normalizeString(job?.source?.goal),
1190
+ problem_statement: normalizeString(summary && summary.problem_statement),
1191
+ verification_plan: normalizeString(summary && summary.verification_plan)
1192
+ });
952
1193
  return {
953
1194
  resolved: domainChain && domainChain.resolved === true,
954
1195
  source: domainChain && domainChain.source ? domainChain.source : 'none',
@@ -957,12 +1198,14 @@ function buildJobDomainChainMetadata(job = {}) {
957
1198
  reason: domainChain && domainChain.reason ? domainChain.reason : null,
958
1199
  decision_path_steps: summary ? Number(summary.decision_path_steps || 0) : 0,
959
1200
  risk_count: summary ? Number(summary.risk_count || 0) : 0,
1201
+ evidence_binding_count: summary ? Number(summary.evidence_binding_count || 0) : 0,
960
1202
  correction_triggers: summary && summary.correction_loop
961
1203
  ? normalizeChainList(summary.correction_loop.triggers, 10)
962
1204
  : [],
963
1205
  verification_gates: summary
964
1206
  ? normalizeChainList(summary.verification_gates, 10)
965
1207
  : [],
1208
+ problem_contract: problemContract,
966
1209
  summary: summary || null,
967
1210
  context: context || null
968
1211
  };
@@ -975,6 +1218,18 @@ function summarizeProblemEvaluation(evaluation = {}) {
975
1218
  confidence_score: Number(evaluation.confidence_score || 0),
976
1219
  risk_level: normalizeString(evaluation?.dimensions?.risk?.level) || 'low',
977
1220
  strategy: normalizeString(evaluation?.dimensions?.strategy?.strategy) || 'direct-execution',
1221
+ contract_score: Number(evaluation?.dimensions?.problem_contract?.score || 0),
1222
+ ontology_score: Number(evaluation?.dimensions?.ontology_alignment?.score || 0),
1223
+ convergence_score: Number(evaluation?.dimensions?.convergence?.score || 0),
1224
+ contract_missing: Array.isArray(evaluation?.dimensions?.problem_contract?.missing)
1225
+ ? evaluation.dimensions.problem_contract.missing
1226
+ : [],
1227
+ ontology_missing_axes: Array.isArray(evaluation?.dimensions?.ontology_alignment?.missing_axes)
1228
+ ? evaluation.dimensions.ontology_alignment.missing_axes
1229
+ : [],
1230
+ convergence_missing: Array.isArray(evaluation?.dimensions?.convergence?.missing)
1231
+ ? evaluation.dimensions.convergence.missing
1232
+ : [],
978
1233
  blockers: Array.isArray(evaluation.blockers) ? evaluation.blockers : [],
979
1234
  warnings: Array.isArray(evaluation.warnings) ? evaluation.warnings : [],
980
1235
  recommendations: Array.isArray(evaluation.recommendations) ? evaluation.recommendations : [],
@@ -1044,6 +1299,7 @@ async function enforceProblemEvaluationForStage(job = {}, stage = '', context =
1044
1299
  goal: normalizeString(context.goal || job?.source?.goal),
1045
1300
  release_channel: normalizeString(context.release_channel || ''),
1046
1301
  domain_chain: context.domain_chain || (job?.source?.domain_chain || {}),
1302
+ problem_contract: context.problem_contract || job?.source?.problem_contract || {},
1047
1303
  related_specs_count: Number(context.related_specs_count || job?.source?.related_specs?.total_candidates || 0),
1048
1304
  stage_readiness: context.stage_readiness || buildStageReadiness(job, stage),
1049
1305
  gate_signals: context.gate_signals || {}
@@ -1127,6 +1383,15 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1127
1383
 
1128
1384
  const jobId = normalizeString(options.job) || createJobId();
1129
1385
  const now = nowIso();
1386
+ const problemContract = normalizeProblemContract(
1387
+ domainChainBinding.problem_contract || {},
1388
+ {
1389
+ scene_id: sceneId,
1390
+ goal: normalizeString(options.goal),
1391
+ problem_statement: normalizeString(domainChainBinding?.summary?.problem_statement),
1392
+ verification_plan: normalizeString(domainChainBinding?.summary?.verification_plan)
1393
+ }
1394
+ );
1130
1395
  const planShadowJob = {
1131
1396
  job_id: jobId,
1132
1397
  scene: {
@@ -1136,6 +1401,8 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1136
1401
  source: {
1137
1402
  goal: normalizeString(options.goal) || null,
1138
1403
  spec_id: domainChainBinding.spec_id || specId || null,
1404
+ problem_contract: problemContract,
1405
+ problem_contract_path: domainChainBinding.problem_contract_path || null,
1139
1406
  domain_chain: {
1140
1407
  resolved: domainChainBinding.resolved === true,
1141
1408
  summary: domainChainBinding.summary || null
@@ -1150,6 +1417,7 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1150
1417
  scene_id: sceneId,
1151
1418
  spec_id: domainChainBinding.spec_id || specId || null,
1152
1419
  goal: normalizeString(options.goal) || null,
1420
+ problem_contract: problemContract,
1153
1421
  domain_chain: {
1154
1422
  resolved: domainChainBinding.resolved === true,
1155
1423
  summary: domainChainBinding.summary || null
@@ -1194,6 +1462,7 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1194
1462
  domain_chain_path: domainChainBinding.chain_path || null,
1195
1463
  domain_chain_summary: domainChainBinding.summary || null,
1196
1464
  domain_chain_reason: domainChainBinding.reason || null,
1465
+ problem_contract: problemContract,
1197
1466
  problem_evaluation: summarizeProblemEvaluation(planProblemEvaluation),
1198
1467
  related_specs_total: Number(relatedSpecLookup.total_candidates || 0),
1199
1468
  related_specs_top: relatedSpecItems
@@ -1210,6 +1479,8 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1210
1479
  from_chat: fromChat,
1211
1480
  goal: normalizeString(options.goal) || null,
1212
1481
  spec_id: domainChainBinding.spec_id || specId || null,
1482
+ problem_contract: problemContract,
1483
+ problem_contract_path: domainChainBinding.problem_contract_path || null,
1213
1484
  domain_chain: {
1214
1485
  resolved: domainChainBinding.resolved === true,
1215
1486
  source: domainChainBinding.source || 'none',
@@ -1267,6 +1538,7 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1267
1538
  domain_chain_source: domainChainBinding.source || 'none',
1268
1539
  domain_chain_spec_id: domainChainBinding.spec_id || null,
1269
1540
  domain_chain_path: domainChainBinding.chain_path || null,
1541
+ problem_contract: problemContract,
1270
1542
  problem_evaluation: summarizeProblemEvaluation(planProblemEvaluation),
1271
1543
  related_specs_total: Number(relatedSpecLookup.total_candidates || 0),
1272
1544
  related_spec_ids: relatedSpecItems.map((item) => item.spec_id)
@@ -1457,16 +1729,19 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
1457
1729
  const autoErrorbookRecords = [];
1458
1730
  const gateSteps = await buildVerifyGateSteps({ profile }, {
1459
1731
  projectPath,
1460
- fileSystem
1732
+ fileSystem,
1733
+ specId: normalizeString(domainChainMetadata.spec_id) || null
1461
1734
  });
1462
1735
  const verifyProblemEvaluation = await enforceProblemEvaluationForStage(job, 'verify', {
1463
1736
  scene_id: normalizeString(job?.scene?.id),
1464
1737
  spec_id: normalizeString(domainChainMetadata.spec_id) || normalizeString(job?.source?.spec_id),
1465
1738
  goal: normalizeString(job?.source?.goal),
1466
1739
  domain_chain: job?.source?.domain_chain || {},
1740
+ problem_contract: job?.source?.problem_contract || {},
1467
1741
  related_specs_count: Number(job?.source?.related_specs?.total_candidates || 0),
1468
1742
  stage_readiness: buildStageReadiness(job, 'verify', {
1469
- gate_required_ready: deriveGateSignals(gateSteps).required_missing === 0
1743
+ gate_required_ready: deriveGateSignals(gateSteps).required_missing === 0,
1744
+ convergence_strict: profile === 'strict'
1470
1745
  }),
1471
1746
  gate_signals: deriveGateSignals(gateSteps)
1472
1747
  }, {
@@ -1609,9 +1884,17 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
1609
1884
  const releaseStartedAt = nowIso();
1610
1885
  const domainChainMetadata = buildJobDomainChainMetadata(job);
1611
1886
  const autoErrorbookRecords = [];
1612
- const gateSteps = await buildReleaseGateSteps({ profile }, {
1887
+ const verifyReportSignals = await readVerifyReportSignals(
1613
1888
  projectPath,
1889
+ normalizeString(job?.artifacts?.verify_report),
1614
1890
  fileSystem
1891
+ );
1892
+ const governanceSignals = await readGovernanceSignals(projectPath, fileSystem);
1893
+ const gateSteps = await buildReleaseGateSteps({ profile }, {
1894
+ projectPath,
1895
+ fileSystem,
1896
+ specId: normalizeString(domainChainMetadata.spec_id) || null,
1897
+ verifyReportPath: normalizeString(job?.artifacts?.verify_report) || null
1615
1898
  });
1616
1899
  const releaseGateSignals = deriveGateSignals(gateSteps);
1617
1900
  const releaseProblemEvaluation = await enforceProblemEvaluationForStage(job, 'release', {
@@ -1620,9 +1903,17 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
1620
1903
  goal: normalizeString(job?.source?.goal),
1621
1904
  release_channel: channel,
1622
1905
  domain_chain: job?.source?.domain_chain || {},
1906
+ problem_contract: job?.source?.problem_contract || {},
1623
1907
  related_specs_count: Number(job?.source?.related_specs?.total_candidates || 0),
1624
1908
  stage_readiness: buildStageReadiness(job, 'release', {
1625
- gate_required_ready: releaseGateSignals.required_missing === 0
1909
+ gate_required_ready: releaseGateSignals.required_missing === 0,
1910
+ convergence_strict: profile === 'strict',
1911
+ verify_stage_passed: isStageCompleted(job, 'verify'),
1912
+ verify_report_ready: verifyReportSignals.available,
1913
+ verify_report_passed: verifyReportSignals.passed,
1914
+ regression_passed: verifyReportSignals.passed && verifyReportSignals.failed_step_count === 0,
1915
+ governance_report_ready: governanceSignals.available,
1916
+ high_alert_count: Number(governanceSignals.high_breach_count || 0)
1626
1917
  }),
1627
1918
  gate_signals: releaseGateSignals
1628
1919
  }, {
@@ -1672,6 +1963,8 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
1672
1963
  passed: gateResult.passed,
1673
1964
  steps: gateResult.steps,
1674
1965
  domain_chain: domainChainMetadata,
1966
+ verify_signals: verifyReportSignals,
1967
+ governance_signals: governanceSignals,
1675
1968
  problem_evaluation: summarizeProblemEvaluation(releaseProblemEvaluation),
1676
1969
  auto_errorbook_records: autoErrorbookRecords
1677
1970
  };