scene-capability-engine 3.3.23 → 3.3.24

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.
@@ -4,6 +4,10 @@ const { spawnSync } = require('child_process');
4
4
  const fs = require('fs-extra');
5
5
  const chalk = require('chalk');
6
6
  const { SessionStore } = require('../runtime/session-store');
7
+ const {
8
+ DOMAIN_CHAIN_RELATIVE_PATH,
9
+ ensureSpecDomainArtifacts
10
+ } = require('../spec/domain-modeling');
7
11
 
8
12
  const STUDIO_JOB_API_VERSION = 'sce.studio.job/v0.1';
9
13
  const STAGE_ORDER = ['plan', 'generate', 'apply', 'verify', 'release'];
@@ -151,7 +155,7 @@ async function autoRecordGateFailure(failure = {}, context = {}, dependencies =
151
155
  const title = `[studio:${stage}] gate failure: ${stepId}`;
152
156
  const tags = ['studio', 'gate-failure', 'release-blocker', `stage-${stage}`];
153
157
  const fingerprint = createGateFailureFingerprint(failure, context);
154
- const specRef = normalizeString(context.scene_id) || jobId;
158
+ const specRef = normalizeString(context.spec_id) || normalizeString(context.scene_id) || jobId;
155
159
 
156
160
  const result = await runErrorbookRecordCommand({
157
161
  title,
@@ -678,6 +682,211 @@ function resolveNextAction(job) {
678
682
  return 'complete';
679
683
  }
680
684
 
685
+ function toRelativePosix(projectPath, absolutePath) {
686
+ return path.relative(projectPath, absolutePath).replace(/\\/g, '/');
687
+ }
688
+
689
+ async function readSpecDomainChain(projectPath, specId, fileSystem = fs) {
690
+ const specRoot = path.join(projectPath, '.sce', 'specs', specId);
691
+ const chainPath = path.join(specRoot, DOMAIN_CHAIN_RELATIVE_PATH);
692
+ if (!await fileSystem.pathExists(chainPath)) {
693
+ return null;
694
+ }
695
+ try {
696
+ const payload = await fileSystem.readJson(chainPath);
697
+ const stat = await fileSystem.stat(chainPath);
698
+ return {
699
+ spec_id: specId,
700
+ chain_path: toRelativePosix(projectPath, chainPath),
701
+ payload,
702
+ updated_at: stat && stat.mtime ? stat.mtime.toISOString() : null,
703
+ mtime_ms: Number(stat && stat.mtimeMs) || 0
704
+ };
705
+ } catch (_error) {
706
+ return null;
707
+ }
708
+ }
709
+
710
+ function normalizeChainList(value, limit = 5) {
711
+ if (!Array.isArray(value)) {
712
+ return [];
713
+ }
714
+ return value
715
+ .filter((item) => item && (typeof item === 'string' || typeof item === 'object'))
716
+ .slice(0, limit)
717
+ .map((item) => {
718
+ if (typeof item === 'string') {
719
+ return normalizeString(item);
720
+ }
721
+ return item;
722
+ });
723
+ }
724
+
725
+ function summarizeDomainChain(payload = {}) {
726
+ const ontology = payload && typeof payload.ontology === 'object' ? payload.ontology : {};
727
+ const decisionPath = Array.isArray(payload.decision_execution_path) ? payload.decision_execution_path : [];
728
+ const correctionLoop = payload && typeof payload.correction_loop === 'object' ? payload.correction_loop : {};
729
+ const verification = payload && typeof payload.verification === 'object' ? payload.verification : {};
730
+ const hypotheses = Array.isArray(payload.hypotheses) ? payload.hypotheses : [];
731
+ const risks = Array.isArray(payload.risks) ? payload.risks : [];
732
+
733
+ return {
734
+ scene_id: normalizeString(payload.scene_id) || null,
735
+ spec_id: normalizeString(payload.spec_id) || null,
736
+ problem_statement: normalizeString(payload?.problem?.statement) || null,
737
+ ontology_counts: {
738
+ entity: Array.isArray(ontology.entity) ? ontology.entity.length : 0,
739
+ relation: Array.isArray(ontology.relation) ? ontology.relation.length : 0,
740
+ business_rule: Array.isArray(ontology.business_rule) ? ontology.business_rule.length : 0,
741
+ decision_policy: Array.isArray(ontology.decision_policy) ? ontology.decision_policy.length : 0,
742
+ execution_flow: Array.isArray(ontology.execution_flow) ? ontology.execution_flow.length : 0
743
+ },
744
+ hypothesis_count: hypotheses.length,
745
+ risk_count: risks.length,
746
+ decision_path_steps: decisionPath.length,
747
+ correction_loop: {
748
+ triggers: normalizeChainList(correctionLoop.triggers, 5),
749
+ actions: normalizeChainList(correctionLoop.actions, 5)
750
+ },
751
+ verification_gates: normalizeChainList(verification.gates, 6)
752
+ };
753
+ }
754
+
755
+ function buildDomainChainRuntimeContext(payload = {}) {
756
+ return {
757
+ scene_id: normalizeString(payload.scene_id) || null,
758
+ spec_id: normalizeString(payload.spec_id) || null,
759
+ problem: {
760
+ statement: normalizeString(payload?.problem?.statement) || null,
761
+ scope: normalizeString(payload?.problem?.scope) || null,
762
+ symptom: normalizeString(payload?.problem?.symptom) || null
763
+ },
764
+ ontology: {
765
+ entity: normalizeChainList(payload?.ontology?.entity, 20),
766
+ relation: normalizeChainList(payload?.ontology?.relation, 20),
767
+ business_rule: normalizeChainList(payload?.ontology?.business_rule, 20),
768
+ decision_policy: normalizeChainList(payload?.ontology?.decision_policy, 20),
769
+ execution_flow: normalizeChainList(payload?.ontology?.execution_flow, 20)
770
+ },
771
+ hypotheses: normalizeChainList(payload.hypotheses, 10),
772
+ risks: normalizeChainList(payload.risks, 10),
773
+ decision_execution_path: normalizeChainList(payload.decision_execution_path, 12),
774
+ correction_loop: {
775
+ triggers: normalizeChainList(payload?.correction_loop?.triggers, 10),
776
+ actions: normalizeChainList(payload?.correction_loop?.actions, 10)
777
+ },
778
+ verification: {
779
+ plan: normalizeString(payload?.verification?.plan) || null,
780
+ gates: normalizeChainList(payload?.verification?.gates, 10)
781
+ }
782
+ };
783
+ }
784
+
785
+ async function resolveSceneDomainChainCandidates(projectPath, sceneId, fileSystem = fs) {
786
+ const specsRoot = path.join(projectPath, '.sce', 'specs');
787
+ if (!await fileSystem.pathExists(specsRoot)) {
788
+ return [];
789
+ }
790
+ const entries = await fileSystem.readdir(specsRoot);
791
+ const candidates = [];
792
+ for (const entry of entries) {
793
+ const specRoot = path.join(specsRoot, entry);
794
+ let stat = null;
795
+ try {
796
+ stat = await fileSystem.stat(specRoot);
797
+ } catch (_error) {
798
+ continue;
799
+ }
800
+ if (!stat || !stat.isDirectory()) {
801
+ continue;
802
+ }
803
+ const chain = await readSpecDomainChain(projectPath, entry, fileSystem);
804
+ if (!chain) {
805
+ continue;
806
+ }
807
+ if (normalizeString(chain?.payload?.scene_id) !== sceneId) {
808
+ continue;
809
+ }
810
+ candidates.push(chain);
811
+ }
812
+ candidates.sort((left, right) => (right.mtime_ms || 0) - (left.mtime_ms || 0));
813
+ return candidates;
814
+ }
815
+
816
+ async function resolveDomainChainBinding(options = {}, dependencies = {}) {
817
+ const projectPath = dependencies.projectPath || process.cwd();
818
+ const fileSystem = dependencies.fileSystem || fs;
819
+ const sceneId = normalizeString(options.sceneId);
820
+ const explicitSpec = normalizeString(options.specId);
821
+ const goal = normalizeString(options.goal);
822
+
823
+ if (explicitSpec) {
824
+ const specRoot = path.join(projectPath, '.sce', 'specs', explicitSpec);
825
+ if (!await fileSystem.pathExists(specRoot)) {
826
+ throw new Error(`--spec not found under .sce/specs: ${explicitSpec}`);
827
+ }
828
+ await ensureSpecDomainArtifacts(projectPath, explicitSpec, {
829
+ fileSystem,
830
+ sceneId,
831
+ problemStatement: goal || `Studio scene cycle for ${sceneId}`
832
+ });
833
+ const chain = await readSpecDomainChain(projectPath, explicitSpec, fileSystem);
834
+ if (!chain) {
835
+ return {
836
+ resolved: false,
837
+ source: 'explicit-spec',
838
+ spec_id: explicitSpec,
839
+ reason: 'domain_chain_missing'
840
+ };
841
+ }
842
+ return {
843
+ resolved: true,
844
+ source: 'explicit-spec',
845
+ spec_id: explicitSpec,
846
+ chain_path: chain.chain_path,
847
+ updated_at: chain.updated_at,
848
+ summary: summarizeDomainChain(chain.payload),
849
+ context: buildDomainChainRuntimeContext(chain.payload)
850
+ };
851
+ }
852
+
853
+ if (!sceneId) {
854
+ return {
855
+ resolved: false,
856
+ source: 'none',
857
+ spec_id: null,
858
+ reason: 'scene_id_missing'
859
+ };
860
+ }
861
+
862
+ const candidates = await resolveSceneDomainChainCandidates(projectPath, sceneId, fileSystem);
863
+ if (candidates.length === 0) {
864
+ return {
865
+ resolved: false,
866
+ source: 'none',
867
+ spec_id: null,
868
+ reason: 'no_scene_bound_domain_chain'
869
+ };
870
+ }
871
+
872
+ const selected = candidates[0];
873
+ return {
874
+ resolved: true,
875
+ source: candidates.length === 1 ? 'scene-auto-single' : 'scene-auto-latest',
876
+ spec_id: selected.spec_id,
877
+ chain_path: selected.chain_path,
878
+ updated_at: selected.updated_at,
879
+ candidate_count: candidates.length,
880
+ candidates: candidates.slice(0, 5).map((item) => ({
881
+ spec_id: item.spec_id,
882
+ chain_path: item.chain_path,
883
+ updated_at: item.updated_at
884
+ })),
885
+ summary: summarizeDomainChain(selected.payload),
886
+ context: buildDomainChainRuntimeContext(selected.payload)
887
+ };
888
+ }
889
+
681
890
  function printStudioPayload(payload, options = {}) {
682
891
  if (options.json) {
683
892
  console.log(JSON.stringify(payload, null, 2));
@@ -731,11 +940,37 @@ function buildCommandPayload(mode, job) {
731
940
  };
732
941
  }
733
942
 
943
+ function buildJobDomainChainMetadata(job = {}) {
944
+ const domainChain = job && job.source && job.source.domain_chain
945
+ ? job.source.domain_chain
946
+ : null;
947
+ const summary = domainChain && domainChain.summary ? domainChain.summary : null;
948
+ const context = domainChain && domainChain.context ? domainChain.context : null;
949
+ return {
950
+ resolved: domainChain && domainChain.resolved === true,
951
+ source: domainChain && domainChain.source ? domainChain.source : 'none',
952
+ spec_id: normalizeString(job?.source?.spec_id) || normalizeString(job?.scene?.spec_id) || null,
953
+ chain_path: domainChain && domainChain.chain_path ? domainChain.chain_path : null,
954
+ reason: domainChain && domainChain.reason ? domainChain.reason : null,
955
+ decision_path_steps: summary ? Number(summary.decision_path_steps || 0) : 0,
956
+ risk_count: summary ? Number(summary.risk_count || 0) : 0,
957
+ correction_triggers: summary && summary.correction_loop
958
+ ? normalizeChainList(summary.correction_loop.triggers, 10)
959
+ : [],
960
+ verification_gates: summary
961
+ ? normalizeChainList(summary.verification_gates, 10)
962
+ : [],
963
+ summary: summary || null,
964
+ context: context || null
965
+ };
966
+ }
967
+
734
968
  async function runStudioPlanCommand(options = {}, dependencies = {}) {
735
969
  const projectPath = dependencies.projectPath || process.cwd();
736
970
  const fileSystem = dependencies.fileSystem || fs;
737
971
  const fromChat = normalizeString(options.fromChat);
738
972
  const sceneId = normalizeString(options.scene);
973
+ const specId = normalizeString(options.spec);
739
974
 
740
975
  if (!fromChat) {
741
976
  throw new Error('--from-chat is required');
@@ -743,6 +978,14 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
743
978
  if (!sceneId) {
744
979
  throw new Error('--scene is required');
745
980
  }
981
+ const domainChainBinding = await resolveDomainChainBinding({
982
+ sceneId,
983
+ specId,
984
+ goal: normalizeString(options.goal)
985
+ }, {
986
+ projectPath,
987
+ fileSystem
988
+ });
746
989
 
747
990
  const paths = resolveStudioPaths(projectPath);
748
991
  await ensureStudioDirectories(paths, fileSystem);
@@ -762,8 +1005,15 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
762
1005
  metadata: {
763
1006
  from_chat: fromChat,
764
1007
  scene_id: sceneId,
1008
+ spec_id: domainChainBinding.spec_id || specId || null,
765
1009
  scene_session_id: sceneSessionBinding.session.session_id,
766
- scene_cycle: sceneSessionBinding.scene_cycle
1010
+ scene_cycle: sceneSessionBinding.scene_cycle,
1011
+ domain_chain_resolved: domainChainBinding.resolved === true,
1012
+ domain_chain_source: domainChainBinding.source || 'none',
1013
+ domain_chain_spec_id: domainChainBinding.spec_id || null,
1014
+ domain_chain_path: domainChainBinding.chain_path || null,
1015
+ domain_chain_summary: domainChainBinding.summary || null,
1016
+ domain_chain_reason: domainChainBinding.reason || null
767
1017
  }
768
1018
  };
769
1019
 
@@ -775,10 +1025,26 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
775
1025
  status: 'planned',
776
1026
  source: {
777
1027
  from_chat: fromChat,
778
- goal: normalizeString(options.goal) || null
1028
+ goal: normalizeString(options.goal) || null,
1029
+ spec_id: domainChainBinding.spec_id || specId || null,
1030
+ domain_chain: {
1031
+ resolved: domainChainBinding.resolved === true,
1032
+ source: domainChainBinding.source || 'none',
1033
+ reason: domainChainBinding.reason || null,
1034
+ spec_id: domainChainBinding.spec_id || null,
1035
+ chain_path: domainChainBinding.chain_path || null,
1036
+ candidate_count: Number.isFinite(Number(domainChainBinding.candidate_count))
1037
+ ? Number(domainChainBinding.candidate_count)
1038
+ : 0,
1039
+ candidates: Array.isArray(domainChainBinding.candidates) ? domainChainBinding.candidates : [],
1040
+ summary: domainChainBinding.summary || null,
1041
+ context: domainChainBinding.context || null,
1042
+ updated_at: domainChainBinding.updated_at || null
1043
+ }
779
1044
  },
780
1045
  scene: {
781
- id: sceneId
1046
+ id: sceneId,
1047
+ spec_id: domainChainBinding.spec_id || specId || null
782
1048
  },
783
1049
  session: {
784
1050
  policy: 'mandatory.scene-primary',
@@ -800,9 +1066,14 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
800
1066
  await appendStudioEvent(paths, job, 'stage.plan.completed', {
801
1067
  from_chat: fromChat,
802
1068
  scene_id: sceneId,
1069
+ spec_id: domainChainBinding.spec_id || specId || null,
803
1070
  scene_session_id: sceneSessionBinding.session.session_id,
804
1071
  scene_cycle: sceneSessionBinding.scene_cycle,
805
- target: job.target
1072
+ target: job.target,
1073
+ domain_chain_resolved: domainChainBinding.resolved === true,
1074
+ domain_chain_source: domainChainBinding.source || 'none',
1075
+ domain_chain_spec_id: domainChainBinding.spec_id || null,
1076
+ domain_chain_path: domainChainBinding.chain_path || null
806
1077
  }, fileSystem);
807
1078
  await writeLatestJob(paths, jobId, fileSystem);
808
1079
 
@@ -836,26 +1107,45 @@ async function runStudioGenerateCommand(options = {}, dependencies = {}) {
836
1107
  }
837
1108
  const sceneId = sceneArg || jobSceneId;
838
1109
  const patchBundleId = normalizeString(options.patchBundle) || `patch-${sceneId}-${Date.now()}`;
1110
+ const domainChainMetadata = buildJobDomainChainMetadata(job);
1111
+ const generateReportPath = `${STUDIO_REPORTS_DIR}/generate-${job.job_id}.json`;
1112
+ const generateReport = {
1113
+ mode: 'studio-generate',
1114
+ api_version: STUDIO_JOB_API_VERSION,
1115
+ job_id: job.job_id,
1116
+ scene_id: sceneId,
1117
+ target: normalizeString(options.target) || job.target || 'default',
1118
+ patch_bundle_id: patchBundleId,
1119
+ generated_at: nowIso(),
1120
+ domain_chain: domainChainMetadata
1121
+ };
1122
+ await writeStudioReport(projectPath, generateReportPath, generateReport, fileSystem);
839
1123
 
840
1124
  job.scene = job.scene || {};
841
1125
  job.scene.id = sceneId;
1126
+ job.scene.spec_id = normalizeString(job?.source?.spec_id) || normalizeString(job?.scene?.spec_id) || null;
842
1127
  job.target = normalizeString(options.target) || job.target || 'default';
843
1128
  job.status = 'generated';
844
1129
  job.artifacts = job.artifacts || {};
845
1130
  job.artifacts.patch_bundle_id = patchBundleId;
1131
+ job.artifacts.generate_report = generateReportPath;
846
1132
  job.updated_at = nowIso();
847
1133
 
848
1134
  ensureStageCompleted(job, 'generate', {
849
1135
  scene_id: sceneId,
850
1136
  target: job.target,
851
- patch_bundle_id: patchBundleId
1137
+ patch_bundle_id: patchBundleId,
1138
+ domain_chain: domainChainMetadata,
1139
+ report: generateReportPath
852
1140
  });
853
1141
 
854
1142
  await saveJob(paths, job, fileSystem);
855
1143
  await appendStudioEvent(paths, job, 'stage.generate.completed', {
856
1144
  scene_id: sceneId,
857
1145
  target: job.target,
858
- patch_bundle_id: patchBundleId
1146
+ patch_bundle_id: patchBundleId,
1147
+ domain_chain: domainChainMetadata,
1148
+ report: generateReportPath
859
1149
  }, fileSystem);
860
1150
  await writeLatestJob(paths, jobId, fileSystem);
861
1151
 
@@ -931,6 +1221,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
931
1221
 
932
1222
  const verifyReportPath = `${STUDIO_REPORTS_DIR}/verify-${job.job_id}.json`;
933
1223
  const verifyStartedAt = nowIso();
1224
+ const domainChainMetadata = buildJobDomainChainMetadata(job);
934
1225
  const autoErrorbookRecords = [];
935
1226
  const gateSteps = await buildVerifyGateSteps({ profile }, {
936
1227
  projectPath,
@@ -946,7 +1237,8 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
946
1237
  stage: 'verify',
947
1238
  profile,
948
1239
  job_id: job.job_id,
949
- scene_id: job?.scene?.id
1240
+ scene_id: job?.scene?.id,
1241
+ spec_id: domainChainMetadata.spec_id
950
1242
  }, {
951
1243
  projectPath,
952
1244
  fileSystem,
@@ -971,6 +1263,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
971
1263
  completed_at: verifyCompletedAt,
972
1264
  passed: gateResult.passed,
973
1265
  steps: gateResult.steps,
1266
+ domain_chain: domainChainMetadata,
974
1267
  auto_errorbook_records: autoErrorbookRecords
975
1268
  };
976
1269
 
@@ -989,6 +1282,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
989
1282
  profile,
990
1283
  passed: false,
991
1284
  report: verifyReportPath,
1285
+ domain_chain: domainChainMetadata,
992
1286
  auto_errorbook_records: autoErrorbookRecords
993
1287
  }
994
1288
  };
@@ -996,6 +1290,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
996
1290
  await appendStudioEvent(paths, job, 'stage.verify.failed', {
997
1291
  profile,
998
1292
  report: verifyReportPath,
1293
+ domain_chain: domainChainMetadata,
999
1294
  auto_errorbook_records: autoErrorbookRecords
1000
1295
  }, fileSystem);
1001
1296
  await writeLatestJob(paths, jobId, fileSystem);
@@ -1007,6 +1302,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
1007
1302
  profile,
1008
1303
  passed: true,
1009
1304
  report: verifyReportPath,
1305
+ domain_chain: domainChainMetadata,
1010
1306
  auto_errorbook_records: autoErrorbookRecords
1011
1307
  });
1012
1308
 
@@ -1015,6 +1311,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
1015
1311
  profile,
1016
1312
  passed: true,
1017
1313
  report: verifyReportPath,
1314
+ domain_chain: domainChainMetadata,
1018
1315
  auto_errorbook_records: autoErrorbookRecords
1019
1316
  }, fileSystem);
1020
1317
  await writeLatestJob(paths, jobId, fileSystem);
@@ -1055,6 +1352,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
1055
1352
  const profile = normalizeString(options.profile) || 'standard';
1056
1353
  const releaseReportPath = `${STUDIO_REPORTS_DIR}/release-${job.job_id}.json`;
1057
1354
  const releaseStartedAt = nowIso();
1355
+ const domainChainMetadata = buildJobDomainChainMetadata(job);
1058
1356
  const autoErrorbookRecords = [];
1059
1357
  const gateSteps = await buildReleaseGateSteps({ profile }, {
1060
1358
  projectPath,
@@ -1070,7 +1368,8 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
1070
1368
  stage: 'release',
1071
1369
  profile,
1072
1370
  job_id: job.job_id,
1073
- scene_id: job?.scene?.id
1371
+ scene_id: job?.scene?.id,
1372
+ spec_id: domainChainMetadata.spec_id
1074
1373
  }, {
1075
1374
  projectPath,
1076
1375
  fileSystem,
@@ -1097,6 +1396,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
1097
1396
  completed_at: releaseCompletedAt,
1098
1397
  passed: gateResult.passed,
1099
1398
  steps: gateResult.steps,
1399
+ domain_chain: domainChainMetadata,
1100
1400
  auto_errorbook_records: autoErrorbookRecords
1101
1401
  };
1102
1402
 
@@ -1118,6 +1418,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
1118
1418
  passed: false,
1119
1419
  report: releaseReportPath,
1120
1420
  auth_required: authResult.required,
1421
+ domain_chain: domainChainMetadata,
1121
1422
  auto_errorbook_records: autoErrorbookRecords
1122
1423
  }
1123
1424
  };
@@ -1127,6 +1428,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
1127
1428
  release_ref: releaseRef,
1128
1429
  report: releaseReportPath,
1129
1430
  auth_required: authResult.required,
1431
+ domain_chain: domainChainMetadata,
1130
1432
  auto_errorbook_records: autoErrorbookRecords
1131
1433
  }, fileSystem);
1132
1434
  await writeLatestJob(paths, jobId, fileSystem);
@@ -1139,6 +1441,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
1139
1441
  release_ref: releaseRef,
1140
1442
  report: releaseReportPath,
1141
1443
  auth_required: authResult.required,
1444
+ domain_chain: domainChainMetadata,
1142
1445
  auto_errorbook_records: autoErrorbookRecords
1143
1446
  });
1144
1447
 
@@ -1169,6 +1472,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
1169
1472
  release_ref: releaseRef,
1170
1473
  report: releaseReportPath,
1171
1474
  auth_required: authResult.required,
1475
+ domain_chain: domainChainMetadata,
1172
1476
  auto_errorbook_records: autoErrorbookRecords
1173
1477
  }, fileSystem);
1174
1478
  await writeLatestJob(paths, jobId, fileSystem);
@@ -1320,6 +1624,7 @@ function registerStudioCommands(program) {
1320
1624
  .description('Create/refresh a studio plan job from chat context')
1321
1625
  .requiredOption('--scene <scene-id>', 'Scene identifier (mandatory primary session anchor)')
1322
1626
  .requiredOption('--from-chat <session>', 'Chat session identifier or transcript reference')
1627
+ .option('--spec <spec-id>', 'Optional spec binding for domain-chain context ingestion')
1323
1628
  .option('--goal <goal>', 'Optional goal summary')
1324
1629
  .option('--target <target>', 'Target integration profile', 'default')
1325
1630
  .option('--job <job-id>', 'Reuse an explicit studio job id')