scene-capability-engine 3.3.23 → 3.3.25
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 +58 -0
- package/bin/scene-capability-engine.js +20 -0
- package/docs/adoption-guide.md +8 -0
- package/docs/autonomous-control-guide.md +8 -8
- package/docs/command-reference.md +41 -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/spec-related.js +70 -0
- package/lib/commands/studio.js +345 -9
- package/lib/spec/domain-modeling.js +439 -0
- package/lib/spec/related-specs.js +260 -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,11 @@ 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');
|
|
11
|
+
const { findRelatedSpecs } = require('../spec/related-specs');
|
|
7
12
|
|
|
8
13
|
const STUDIO_JOB_API_VERSION = 'sce.studio.job/v0.1';
|
|
9
14
|
const STAGE_ORDER = ['plan', 'generate', 'apply', 'verify', 'release'];
|
|
@@ -151,7 +156,7 @@ async function autoRecordGateFailure(failure = {}, context = {}, dependencies =
|
|
|
151
156
|
const title = `[studio:${stage}] gate failure: ${stepId}`;
|
|
152
157
|
const tags = ['studio', 'gate-failure', 'release-blocker', `stage-${stage}`];
|
|
153
158
|
const fingerprint = createGateFailureFingerprint(failure, context);
|
|
154
|
-
const specRef = normalizeString(context.scene_id) || jobId;
|
|
159
|
+
const specRef = normalizeString(context.spec_id) || normalizeString(context.scene_id) || jobId;
|
|
155
160
|
|
|
156
161
|
const result = await runErrorbookRecordCommand({
|
|
157
162
|
title,
|
|
@@ -678,6 +683,211 @@ function resolveNextAction(job) {
|
|
|
678
683
|
return 'complete';
|
|
679
684
|
}
|
|
680
685
|
|
|
686
|
+
function toRelativePosix(projectPath, absolutePath) {
|
|
687
|
+
return path.relative(projectPath, absolutePath).replace(/\\/g, '/');
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
async function readSpecDomainChain(projectPath, specId, fileSystem = fs) {
|
|
691
|
+
const specRoot = path.join(projectPath, '.sce', 'specs', specId);
|
|
692
|
+
const chainPath = path.join(specRoot, DOMAIN_CHAIN_RELATIVE_PATH);
|
|
693
|
+
if (!await fileSystem.pathExists(chainPath)) {
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
try {
|
|
697
|
+
const payload = await fileSystem.readJson(chainPath);
|
|
698
|
+
const stat = await fileSystem.stat(chainPath);
|
|
699
|
+
return {
|
|
700
|
+
spec_id: specId,
|
|
701
|
+
chain_path: toRelativePosix(projectPath, chainPath),
|
|
702
|
+
payload,
|
|
703
|
+
updated_at: stat && stat.mtime ? stat.mtime.toISOString() : null,
|
|
704
|
+
mtime_ms: Number(stat && stat.mtimeMs) || 0
|
|
705
|
+
};
|
|
706
|
+
} catch (_error) {
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function normalizeChainList(value, limit = 5) {
|
|
712
|
+
if (!Array.isArray(value)) {
|
|
713
|
+
return [];
|
|
714
|
+
}
|
|
715
|
+
return value
|
|
716
|
+
.filter((item) => item && (typeof item === 'string' || typeof item === 'object'))
|
|
717
|
+
.slice(0, limit)
|
|
718
|
+
.map((item) => {
|
|
719
|
+
if (typeof item === 'string') {
|
|
720
|
+
return normalizeString(item);
|
|
721
|
+
}
|
|
722
|
+
return item;
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function summarizeDomainChain(payload = {}) {
|
|
727
|
+
const ontology = payload && typeof payload.ontology === 'object' ? payload.ontology : {};
|
|
728
|
+
const decisionPath = Array.isArray(payload.decision_execution_path) ? payload.decision_execution_path : [];
|
|
729
|
+
const correctionLoop = payload && typeof payload.correction_loop === 'object' ? payload.correction_loop : {};
|
|
730
|
+
const verification = payload && typeof payload.verification === 'object' ? payload.verification : {};
|
|
731
|
+
const hypotheses = Array.isArray(payload.hypotheses) ? payload.hypotheses : [];
|
|
732
|
+
const risks = Array.isArray(payload.risks) ? payload.risks : [];
|
|
733
|
+
|
|
734
|
+
return {
|
|
735
|
+
scene_id: normalizeString(payload.scene_id) || null,
|
|
736
|
+
spec_id: normalizeString(payload.spec_id) || null,
|
|
737
|
+
problem_statement: normalizeString(payload?.problem?.statement) || null,
|
|
738
|
+
ontology_counts: {
|
|
739
|
+
entity: Array.isArray(ontology.entity) ? ontology.entity.length : 0,
|
|
740
|
+
relation: Array.isArray(ontology.relation) ? ontology.relation.length : 0,
|
|
741
|
+
business_rule: Array.isArray(ontology.business_rule) ? ontology.business_rule.length : 0,
|
|
742
|
+
decision_policy: Array.isArray(ontology.decision_policy) ? ontology.decision_policy.length : 0,
|
|
743
|
+
execution_flow: Array.isArray(ontology.execution_flow) ? ontology.execution_flow.length : 0
|
|
744
|
+
},
|
|
745
|
+
hypothesis_count: hypotheses.length,
|
|
746
|
+
risk_count: risks.length,
|
|
747
|
+
decision_path_steps: decisionPath.length,
|
|
748
|
+
correction_loop: {
|
|
749
|
+
triggers: normalizeChainList(correctionLoop.triggers, 5),
|
|
750
|
+
actions: normalizeChainList(correctionLoop.actions, 5)
|
|
751
|
+
},
|
|
752
|
+
verification_gates: normalizeChainList(verification.gates, 6)
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function buildDomainChainRuntimeContext(payload = {}) {
|
|
757
|
+
return {
|
|
758
|
+
scene_id: normalizeString(payload.scene_id) || null,
|
|
759
|
+
spec_id: normalizeString(payload.spec_id) || null,
|
|
760
|
+
problem: {
|
|
761
|
+
statement: normalizeString(payload?.problem?.statement) || null,
|
|
762
|
+
scope: normalizeString(payload?.problem?.scope) || null,
|
|
763
|
+
symptom: normalizeString(payload?.problem?.symptom) || null
|
|
764
|
+
},
|
|
765
|
+
ontology: {
|
|
766
|
+
entity: normalizeChainList(payload?.ontology?.entity, 20),
|
|
767
|
+
relation: normalizeChainList(payload?.ontology?.relation, 20),
|
|
768
|
+
business_rule: normalizeChainList(payload?.ontology?.business_rule, 20),
|
|
769
|
+
decision_policy: normalizeChainList(payload?.ontology?.decision_policy, 20),
|
|
770
|
+
execution_flow: normalizeChainList(payload?.ontology?.execution_flow, 20)
|
|
771
|
+
},
|
|
772
|
+
hypotheses: normalizeChainList(payload.hypotheses, 10),
|
|
773
|
+
risks: normalizeChainList(payload.risks, 10),
|
|
774
|
+
decision_execution_path: normalizeChainList(payload.decision_execution_path, 12),
|
|
775
|
+
correction_loop: {
|
|
776
|
+
triggers: normalizeChainList(payload?.correction_loop?.triggers, 10),
|
|
777
|
+
actions: normalizeChainList(payload?.correction_loop?.actions, 10)
|
|
778
|
+
},
|
|
779
|
+
verification: {
|
|
780
|
+
plan: normalizeString(payload?.verification?.plan) || null,
|
|
781
|
+
gates: normalizeChainList(payload?.verification?.gates, 10)
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
async function resolveSceneDomainChainCandidates(projectPath, sceneId, fileSystem = fs) {
|
|
787
|
+
const specsRoot = path.join(projectPath, '.sce', 'specs');
|
|
788
|
+
if (!await fileSystem.pathExists(specsRoot)) {
|
|
789
|
+
return [];
|
|
790
|
+
}
|
|
791
|
+
const entries = await fileSystem.readdir(specsRoot);
|
|
792
|
+
const candidates = [];
|
|
793
|
+
for (const entry of entries) {
|
|
794
|
+
const specRoot = path.join(specsRoot, entry);
|
|
795
|
+
let stat = null;
|
|
796
|
+
try {
|
|
797
|
+
stat = await fileSystem.stat(specRoot);
|
|
798
|
+
} catch (_error) {
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
if (!stat || !stat.isDirectory()) {
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
const chain = await readSpecDomainChain(projectPath, entry, fileSystem);
|
|
805
|
+
if (!chain) {
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
if (normalizeString(chain?.payload?.scene_id) !== sceneId) {
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
candidates.push(chain);
|
|
812
|
+
}
|
|
813
|
+
candidates.sort((left, right) => (right.mtime_ms || 0) - (left.mtime_ms || 0));
|
|
814
|
+
return candidates;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
async function resolveDomainChainBinding(options = {}, dependencies = {}) {
|
|
818
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
819
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
820
|
+
const sceneId = normalizeString(options.sceneId);
|
|
821
|
+
const explicitSpec = normalizeString(options.specId);
|
|
822
|
+
const goal = normalizeString(options.goal);
|
|
823
|
+
|
|
824
|
+
if (explicitSpec) {
|
|
825
|
+
const specRoot = path.join(projectPath, '.sce', 'specs', explicitSpec);
|
|
826
|
+
if (!await fileSystem.pathExists(specRoot)) {
|
|
827
|
+
throw new Error(`--spec not found under .sce/specs: ${explicitSpec}`);
|
|
828
|
+
}
|
|
829
|
+
await ensureSpecDomainArtifacts(projectPath, explicitSpec, {
|
|
830
|
+
fileSystem,
|
|
831
|
+
sceneId,
|
|
832
|
+
problemStatement: goal || `Studio scene cycle for ${sceneId}`
|
|
833
|
+
});
|
|
834
|
+
const chain = await readSpecDomainChain(projectPath, explicitSpec, fileSystem);
|
|
835
|
+
if (!chain) {
|
|
836
|
+
return {
|
|
837
|
+
resolved: false,
|
|
838
|
+
source: 'explicit-spec',
|
|
839
|
+
spec_id: explicitSpec,
|
|
840
|
+
reason: 'domain_chain_missing'
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
return {
|
|
844
|
+
resolved: true,
|
|
845
|
+
source: 'explicit-spec',
|
|
846
|
+
spec_id: explicitSpec,
|
|
847
|
+
chain_path: chain.chain_path,
|
|
848
|
+
updated_at: chain.updated_at,
|
|
849
|
+
summary: summarizeDomainChain(chain.payload),
|
|
850
|
+
context: buildDomainChainRuntimeContext(chain.payload)
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
if (!sceneId) {
|
|
855
|
+
return {
|
|
856
|
+
resolved: false,
|
|
857
|
+
source: 'none',
|
|
858
|
+
spec_id: null,
|
|
859
|
+
reason: 'scene_id_missing'
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const candidates = await resolveSceneDomainChainCandidates(projectPath, sceneId, fileSystem);
|
|
864
|
+
if (candidates.length === 0) {
|
|
865
|
+
return {
|
|
866
|
+
resolved: false,
|
|
867
|
+
source: 'none',
|
|
868
|
+
spec_id: null,
|
|
869
|
+
reason: 'no_scene_bound_domain_chain'
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
const selected = candidates[0];
|
|
874
|
+
return {
|
|
875
|
+
resolved: true,
|
|
876
|
+
source: candidates.length === 1 ? 'scene-auto-single' : 'scene-auto-latest',
|
|
877
|
+
spec_id: selected.spec_id,
|
|
878
|
+
chain_path: selected.chain_path,
|
|
879
|
+
updated_at: selected.updated_at,
|
|
880
|
+
candidate_count: candidates.length,
|
|
881
|
+
candidates: candidates.slice(0, 5).map((item) => ({
|
|
882
|
+
spec_id: item.spec_id,
|
|
883
|
+
chain_path: item.chain_path,
|
|
884
|
+
updated_at: item.updated_at
|
|
885
|
+
})),
|
|
886
|
+
summary: summarizeDomainChain(selected.payload),
|
|
887
|
+
context: buildDomainChainRuntimeContext(selected.payload)
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
|
|
681
891
|
function printStudioPayload(payload, options = {}) {
|
|
682
892
|
if (options.json) {
|
|
683
893
|
console.log(JSON.stringify(payload, null, 2));
|
|
@@ -731,11 +941,37 @@ function buildCommandPayload(mode, job) {
|
|
|
731
941
|
};
|
|
732
942
|
}
|
|
733
943
|
|
|
944
|
+
function buildJobDomainChainMetadata(job = {}) {
|
|
945
|
+
const domainChain = job && job.source && job.source.domain_chain
|
|
946
|
+
? job.source.domain_chain
|
|
947
|
+
: null;
|
|
948
|
+
const summary = domainChain && domainChain.summary ? domainChain.summary : null;
|
|
949
|
+
const context = domainChain && domainChain.context ? domainChain.context : null;
|
|
950
|
+
return {
|
|
951
|
+
resolved: domainChain && domainChain.resolved === true,
|
|
952
|
+
source: domainChain && domainChain.source ? domainChain.source : 'none',
|
|
953
|
+
spec_id: normalizeString(job?.source?.spec_id) || normalizeString(job?.scene?.spec_id) || null,
|
|
954
|
+
chain_path: domainChain && domainChain.chain_path ? domainChain.chain_path : null,
|
|
955
|
+
reason: domainChain && domainChain.reason ? domainChain.reason : null,
|
|
956
|
+
decision_path_steps: summary ? Number(summary.decision_path_steps || 0) : 0,
|
|
957
|
+
risk_count: summary ? Number(summary.risk_count || 0) : 0,
|
|
958
|
+
correction_triggers: summary && summary.correction_loop
|
|
959
|
+
? normalizeChainList(summary.correction_loop.triggers, 10)
|
|
960
|
+
: [],
|
|
961
|
+
verification_gates: summary
|
|
962
|
+
? normalizeChainList(summary.verification_gates, 10)
|
|
963
|
+
: [],
|
|
964
|
+
summary: summary || null,
|
|
965
|
+
context: context || null
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
|
|
734
969
|
async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
735
970
|
const projectPath = dependencies.projectPath || process.cwd();
|
|
736
971
|
const fileSystem = dependencies.fileSystem || fs;
|
|
737
972
|
const fromChat = normalizeString(options.fromChat);
|
|
738
973
|
const sceneId = normalizeString(options.scene);
|
|
974
|
+
const specId = normalizeString(options.spec);
|
|
739
975
|
|
|
740
976
|
if (!fromChat) {
|
|
741
977
|
throw new Error('--from-chat is required');
|
|
@@ -743,6 +979,33 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
743
979
|
if (!sceneId) {
|
|
744
980
|
throw new Error('--scene is required');
|
|
745
981
|
}
|
|
982
|
+
const domainChainBinding = await resolveDomainChainBinding({
|
|
983
|
+
sceneId,
|
|
984
|
+
specId,
|
|
985
|
+
goal: normalizeString(options.goal)
|
|
986
|
+
}, {
|
|
987
|
+
projectPath,
|
|
988
|
+
fileSystem
|
|
989
|
+
});
|
|
990
|
+
const relatedSpecLookup = await findRelatedSpecs({
|
|
991
|
+
query: normalizeString(options.goal),
|
|
992
|
+
sceneId,
|
|
993
|
+
limit: 8,
|
|
994
|
+
excludeSpecId: domainChainBinding.spec_id || specId || null
|
|
995
|
+
}, {
|
|
996
|
+
projectPath,
|
|
997
|
+
fileSystem
|
|
998
|
+
});
|
|
999
|
+
const relatedSpecItems = Array.isArray(relatedSpecLookup.related_specs)
|
|
1000
|
+
? relatedSpecLookup.related_specs.map((item) => ({
|
|
1001
|
+
spec_id: item.spec_id,
|
|
1002
|
+
scene_id: item.scene_id || null,
|
|
1003
|
+
score: Number(item.score || 0),
|
|
1004
|
+
reasons: Array.isArray(item.reasons) ? item.reasons : [],
|
|
1005
|
+
matched_tokens: Array.isArray(item.matched_tokens) ? item.matched_tokens : [],
|
|
1006
|
+
updated_at: item.updated_at || null
|
|
1007
|
+
}))
|
|
1008
|
+
: [];
|
|
746
1009
|
|
|
747
1010
|
const paths = resolveStudioPaths(projectPath);
|
|
748
1011
|
await ensureStudioDirectories(paths, fileSystem);
|
|
@@ -762,8 +1025,17 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
762
1025
|
metadata: {
|
|
763
1026
|
from_chat: fromChat,
|
|
764
1027
|
scene_id: sceneId,
|
|
1028
|
+
spec_id: domainChainBinding.spec_id || specId || null,
|
|
765
1029
|
scene_session_id: sceneSessionBinding.session.session_id,
|
|
766
|
-
scene_cycle: sceneSessionBinding.scene_cycle
|
|
1030
|
+
scene_cycle: sceneSessionBinding.scene_cycle,
|
|
1031
|
+
domain_chain_resolved: domainChainBinding.resolved === true,
|
|
1032
|
+
domain_chain_source: domainChainBinding.source || 'none',
|
|
1033
|
+
domain_chain_spec_id: domainChainBinding.spec_id || null,
|
|
1034
|
+
domain_chain_path: domainChainBinding.chain_path || null,
|
|
1035
|
+
domain_chain_summary: domainChainBinding.summary || null,
|
|
1036
|
+
domain_chain_reason: domainChainBinding.reason || null,
|
|
1037
|
+
related_specs_total: Number(relatedSpecLookup.total_candidates || 0),
|
|
1038
|
+
related_specs_top: relatedSpecItems
|
|
767
1039
|
}
|
|
768
1040
|
};
|
|
769
1041
|
|
|
@@ -775,10 +1047,33 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
775
1047
|
status: 'planned',
|
|
776
1048
|
source: {
|
|
777
1049
|
from_chat: fromChat,
|
|
778
|
-
goal: normalizeString(options.goal) || null
|
|
1050
|
+
goal: normalizeString(options.goal) || null,
|
|
1051
|
+
spec_id: domainChainBinding.spec_id || specId || null,
|
|
1052
|
+
domain_chain: {
|
|
1053
|
+
resolved: domainChainBinding.resolved === true,
|
|
1054
|
+
source: domainChainBinding.source || 'none',
|
|
1055
|
+
reason: domainChainBinding.reason || null,
|
|
1056
|
+
spec_id: domainChainBinding.spec_id || null,
|
|
1057
|
+
chain_path: domainChainBinding.chain_path || null,
|
|
1058
|
+
candidate_count: Number.isFinite(Number(domainChainBinding.candidate_count))
|
|
1059
|
+
? Number(domainChainBinding.candidate_count)
|
|
1060
|
+
: 0,
|
|
1061
|
+
candidates: Array.isArray(domainChainBinding.candidates) ? domainChainBinding.candidates : [],
|
|
1062
|
+
summary: domainChainBinding.summary || null,
|
|
1063
|
+
context: domainChainBinding.context || null,
|
|
1064
|
+
updated_at: domainChainBinding.updated_at || null
|
|
1065
|
+
},
|
|
1066
|
+
related_specs: {
|
|
1067
|
+
query: relatedSpecLookup.query || '',
|
|
1068
|
+
scene_id: relatedSpecLookup.scene_id || null,
|
|
1069
|
+
total_candidates: Number(relatedSpecLookup.total_candidates || 0),
|
|
1070
|
+
items: relatedSpecItems
|
|
1071
|
+
}
|
|
779
1072
|
},
|
|
780
1073
|
scene: {
|
|
781
|
-
id: sceneId
|
|
1074
|
+
id: sceneId,
|
|
1075
|
+
spec_id: domainChainBinding.spec_id || specId || null,
|
|
1076
|
+
related_spec_ids: relatedSpecItems.map((item) => item.spec_id)
|
|
782
1077
|
},
|
|
783
1078
|
session: {
|
|
784
1079
|
policy: 'mandatory.scene-primary',
|
|
@@ -800,9 +1095,16 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
800
1095
|
await appendStudioEvent(paths, job, 'stage.plan.completed', {
|
|
801
1096
|
from_chat: fromChat,
|
|
802
1097
|
scene_id: sceneId,
|
|
1098
|
+
spec_id: domainChainBinding.spec_id || specId || null,
|
|
803
1099
|
scene_session_id: sceneSessionBinding.session.session_id,
|
|
804
1100
|
scene_cycle: sceneSessionBinding.scene_cycle,
|
|
805
|
-
target: job.target
|
|
1101
|
+
target: job.target,
|
|
1102
|
+
domain_chain_resolved: domainChainBinding.resolved === true,
|
|
1103
|
+
domain_chain_source: domainChainBinding.source || 'none',
|
|
1104
|
+
domain_chain_spec_id: domainChainBinding.spec_id || null,
|
|
1105
|
+
domain_chain_path: domainChainBinding.chain_path || null,
|
|
1106
|
+
related_specs_total: Number(relatedSpecLookup.total_candidates || 0),
|
|
1107
|
+
related_spec_ids: relatedSpecItems.map((item) => item.spec_id)
|
|
806
1108
|
}, fileSystem);
|
|
807
1109
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
808
1110
|
|
|
@@ -836,26 +1138,45 @@ async function runStudioGenerateCommand(options = {}, dependencies = {}) {
|
|
|
836
1138
|
}
|
|
837
1139
|
const sceneId = sceneArg || jobSceneId;
|
|
838
1140
|
const patchBundleId = normalizeString(options.patchBundle) || `patch-${sceneId}-${Date.now()}`;
|
|
1141
|
+
const domainChainMetadata = buildJobDomainChainMetadata(job);
|
|
1142
|
+
const generateReportPath = `${STUDIO_REPORTS_DIR}/generate-${job.job_id}.json`;
|
|
1143
|
+
const generateReport = {
|
|
1144
|
+
mode: 'studio-generate',
|
|
1145
|
+
api_version: STUDIO_JOB_API_VERSION,
|
|
1146
|
+
job_id: job.job_id,
|
|
1147
|
+
scene_id: sceneId,
|
|
1148
|
+
target: normalizeString(options.target) || job.target || 'default',
|
|
1149
|
+
patch_bundle_id: patchBundleId,
|
|
1150
|
+
generated_at: nowIso(),
|
|
1151
|
+
domain_chain: domainChainMetadata
|
|
1152
|
+
};
|
|
1153
|
+
await writeStudioReport(projectPath, generateReportPath, generateReport, fileSystem);
|
|
839
1154
|
|
|
840
1155
|
job.scene = job.scene || {};
|
|
841
1156
|
job.scene.id = sceneId;
|
|
1157
|
+
job.scene.spec_id = normalizeString(job?.source?.spec_id) || normalizeString(job?.scene?.spec_id) || null;
|
|
842
1158
|
job.target = normalizeString(options.target) || job.target || 'default';
|
|
843
1159
|
job.status = 'generated';
|
|
844
1160
|
job.artifacts = job.artifacts || {};
|
|
845
1161
|
job.artifacts.patch_bundle_id = patchBundleId;
|
|
1162
|
+
job.artifacts.generate_report = generateReportPath;
|
|
846
1163
|
job.updated_at = nowIso();
|
|
847
1164
|
|
|
848
1165
|
ensureStageCompleted(job, 'generate', {
|
|
849
1166
|
scene_id: sceneId,
|
|
850
1167
|
target: job.target,
|
|
851
|
-
patch_bundle_id: patchBundleId
|
|
1168
|
+
patch_bundle_id: patchBundleId,
|
|
1169
|
+
domain_chain: domainChainMetadata,
|
|
1170
|
+
report: generateReportPath
|
|
852
1171
|
});
|
|
853
1172
|
|
|
854
1173
|
await saveJob(paths, job, fileSystem);
|
|
855
1174
|
await appendStudioEvent(paths, job, 'stage.generate.completed', {
|
|
856
1175
|
scene_id: sceneId,
|
|
857
1176
|
target: job.target,
|
|
858
|
-
patch_bundle_id: patchBundleId
|
|
1177
|
+
patch_bundle_id: patchBundleId,
|
|
1178
|
+
domain_chain: domainChainMetadata,
|
|
1179
|
+
report: generateReportPath
|
|
859
1180
|
}, fileSystem);
|
|
860
1181
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
861
1182
|
|
|
@@ -931,6 +1252,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
|
|
|
931
1252
|
|
|
932
1253
|
const verifyReportPath = `${STUDIO_REPORTS_DIR}/verify-${job.job_id}.json`;
|
|
933
1254
|
const verifyStartedAt = nowIso();
|
|
1255
|
+
const domainChainMetadata = buildJobDomainChainMetadata(job);
|
|
934
1256
|
const autoErrorbookRecords = [];
|
|
935
1257
|
const gateSteps = await buildVerifyGateSteps({ profile }, {
|
|
936
1258
|
projectPath,
|
|
@@ -946,7 +1268,8 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
|
|
|
946
1268
|
stage: 'verify',
|
|
947
1269
|
profile,
|
|
948
1270
|
job_id: job.job_id,
|
|
949
|
-
scene_id: job?.scene?.id
|
|
1271
|
+
scene_id: job?.scene?.id,
|
|
1272
|
+
spec_id: domainChainMetadata.spec_id
|
|
950
1273
|
}, {
|
|
951
1274
|
projectPath,
|
|
952
1275
|
fileSystem,
|
|
@@ -971,6 +1294,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
|
|
|
971
1294
|
completed_at: verifyCompletedAt,
|
|
972
1295
|
passed: gateResult.passed,
|
|
973
1296
|
steps: gateResult.steps,
|
|
1297
|
+
domain_chain: domainChainMetadata,
|
|
974
1298
|
auto_errorbook_records: autoErrorbookRecords
|
|
975
1299
|
};
|
|
976
1300
|
|
|
@@ -989,6 +1313,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
|
|
|
989
1313
|
profile,
|
|
990
1314
|
passed: false,
|
|
991
1315
|
report: verifyReportPath,
|
|
1316
|
+
domain_chain: domainChainMetadata,
|
|
992
1317
|
auto_errorbook_records: autoErrorbookRecords
|
|
993
1318
|
}
|
|
994
1319
|
};
|
|
@@ -996,6 +1321,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
|
|
|
996
1321
|
await appendStudioEvent(paths, job, 'stage.verify.failed', {
|
|
997
1322
|
profile,
|
|
998
1323
|
report: verifyReportPath,
|
|
1324
|
+
domain_chain: domainChainMetadata,
|
|
999
1325
|
auto_errorbook_records: autoErrorbookRecords
|
|
1000
1326
|
}, fileSystem);
|
|
1001
1327
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
@@ -1007,6 +1333,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
|
|
|
1007
1333
|
profile,
|
|
1008
1334
|
passed: true,
|
|
1009
1335
|
report: verifyReportPath,
|
|
1336
|
+
domain_chain: domainChainMetadata,
|
|
1010
1337
|
auto_errorbook_records: autoErrorbookRecords
|
|
1011
1338
|
});
|
|
1012
1339
|
|
|
@@ -1015,6 +1342,7 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
|
|
|
1015
1342
|
profile,
|
|
1016
1343
|
passed: true,
|
|
1017
1344
|
report: verifyReportPath,
|
|
1345
|
+
domain_chain: domainChainMetadata,
|
|
1018
1346
|
auto_errorbook_records: autoErrorbookRecords
|
|
1019
1347
|
}, fileSystem);
|
|
1020
1348
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
@@ -1055,6 +1383,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
1055
1383
|
const profile = normalizeString(options.profile) || 'standard';
|
|
1056
1384
|
const releaseReportPath = `${STUDIO_REPORTS_DIR}/release-${job.job_id}.json`;
|
|
1057
1385
|
const releaseStartedAt = nowIso();
|
|
1386
|
+
const domainChainMetadata = buildJobDomainChainMetadata(job);
|
|
1058
1387
|
const autoErrorbookRecords = [];
|
|
1059
1388
|
const gateSteps = await buildReleaseGateSteps({ profile }, {
|
|
1060
1389
|
projectPath,
|
|
@@ -1070,7 +1399,8 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
1070
1399
|
stage: 'release',
|
|
1071
1400
|
profile,
|
|
1072
1401
|
job_id: job.job_id,
|
|
1073
|
-
scene_id: job?.scene?.id
|
|
1402
|
+
scene_id: job?.scene?.id,
|
|
1403
|
+
spec_id: domainChainMetadata.spec_id
|
|
1074
1404
|
}, {
|
|
1075
1405
|
projectPath,
|
|
1076
1406
|
fileSystem,
|
|
@@ -1097,6 +1427,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
1097
1427
|
completed_at: releaseCompletedAt,
|
|
1098
1428
|
passed: gateResult.passed,
|
|
1099
1429
|
steps: gateResult.steps,
|
|
1430
|
+
domain_chain: domainChainMetadata,
|
|
1100
1431
|
auto_errorbook_records: autoErrorbookRecords
|
|
1101
1432
|
};
|
|
1102
1433
|
|
|
@@ -1118,6 +1449,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
1118
1449
|
passed: false,
|
|
1119
1450
|
report: releaseReportPath,
|
|
1120
1451
|
auth_required: authResult.required,
|
|
1452
|
+
domain_chain: domainChainMetadata,
|
|
1121
1453
|
auto_errorbook_records: autoErrorbookRecords
|
|
1122
1454
|
}
|
|
1123
1455
|
};
|
|
@@ -1127,6 +1459,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
1127
1459
|
release_ref: releaseRef,
|
|
1128
1460
|
report: releaseReportPath,
|
|
1129
1461
|
auth_required: authResult.required,
|
|
1462
|
+
domain_chain: domainChainMetadata,
|
|
1130
1463
|
auto_errorbook_records: autoErrorbookRecords
|
|
1131
1464
|
}, fileSystem);
|
|
1132
1465
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
@@ -1139,6 +1472,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
1139
1472
|
release_ref: releaseRef,
|
|
1140
1473
|
report: releaseReportPath,
|
|
1141
1474
|
auth_required: authResult.required,
|
|
1475
|
+
domain_chain: domainChainMetadata,
|
|
1142
1476
|
auto_errorbook_records: autoErrorbookRecords
|
|
1143
1477
|
});
|
|
1144
1478
|
|
|
@@ -1169,6 +1503,7 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
1169
1503
|
release_ref: releaseRef,
|
|
1170
1504
|
report: releaseReportPath,
|
|
1171
1505
|
auth_required: authResult.required,
|
|
1506
|
+
domain_chain: domainChainMetadata,
|
|
1172
1507
|
auto_errorbook_records: autoErrorbookRecords
|
|
1173
1508
|
}, fileSystem);
|
|
1174
1509
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
@@ -1320,6 +1655,7 @@ function registerStudioCommands(program) {
|
|
|
1320
1655
|
.description('Create/refresh a studio plan job from chat context')
|
|
1321
1656
|
.requiredOption('--scene <scene-id>', 'Scene identifier (mandatory primary session anchor)')
|
|
1322
1657
|
.requiredOption('--from-chat <session>', 'Chat session identifier or transcript reference')
|
|
1658
|
+
.option('--spec <spec-id>', 'Optional spec binding for domain-chain context ingestion')
|
|
1323
1659
|
.option('--goal <goal>', 'Optional goal summary')
|
|
1324
1660
|
.option('--target <target>', 'Target integration profile', 'default')
|
|
1325
1661
|
.option('--job <job-id>', 'Reuse an explicit studio job id')
|