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.
- package/CHANGELOG.md +54 -0
- package/bin/scene-capability-engine.js +10 -0
- package/docs/adoption-guide.md +8 -0
- package/docs/autonomous-control-guide.md +8 -8
- package/docs/command-reference.md +36 -2
- package/docs/errorbook-registry.md +12 -0
- package/lib/auto/config-schema.js +7 -7
- package/lib/commands/auto.js +2 -2
- package/lib/commands/errorbook.js +258 -0
- package/lib/commands/spec-bootstrap.js +17 -2
- package/lib/commands/spec-domain.js +217 -0
- package/lib/commands/studio.js +314 -9
- package/lib/spec/domain-modeling.js +439 -0
- package/lib/spec-gate/policy/default-policy.js +1 -0
- package/lib/spec-gate/rules/default-rules.js +8 -0
- package/package.json +3 -2
- package/template/.sce/steering/CORE_PRINCIPLES.md +30 -1
package/lib/commands/studio.js
CHANGED
|
@@ -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')
|