scene-capability-engine 3.4.5 → 3.5.0
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 +40 -0
- package/README.md +31 -1
- package/README.zh.md +31 -1
- package/docs/command-reference.md +73 -7
- package/docs/images/wechat-qr.png +0 -0
- package/lib/adoption/adoption-strategy.js +2 -0
- package/lib/adoption/detection-engine.js +2 -0
- package/lib/adoption/file-classifier.js +3 -1
- package/lib/adoption/smart-orchestrator.js +2 -0
- package/lib/commands/errorbook.js +142 -2
- package/lib/commands/studio.js +569 -25
- package/lib/problem/problem-evaluator.js +431 -16
- package/lib/spec/domain-modeling.js +49 -4
- package/lib/studio/spec-intake-governor.js +992 -0
- package/lib/workspace/takeover-baseline.js +128 -4
- package/package.json +1 -1
- package/template/.sce/config/problem-closure-policy.json +18 -0
- package/template/.sce/config/problem-eval-policy.json +43 -1
- package/template/.sce/config/spec-domain-policy.json +9 -1
- package/template/.sce/config/studio-intake-policy.json +68 -0
- package/template/.sce/config/takeover-baseline.json +88 -1
- package/template/.sce/steering/CORE_PRINCIPLES.md +4 -2
package/lib/commands/studio.js
CHANGED
|
@@ -11,6 +11,10 @@ const {
|
|
|
11
11
|
const { findRelatedSpecs } = require('../spec/related-specs');
|
|
12
12
|
const { captureTimelineCheckpoint } = require('../runtime/project-timeline');
|
|
13
13
|
const { runProblemEvaluation } = require('../problem/problem-evaluator');
|
|
14
|
+
const {
|
|
15
|
+
runStudioAutoIntake,
|
|
16
|
+
runStudioSpecGovernance
|
|
17
|
+
} = require('../studio/spec-intake-governor');
|
|
14
18
|
|
|
15
19
|
const STUDIO_JOB_API_VERSION = 'sce.studio.job/v0.1';
|
|
16
20
|
const STAGE_ORDER = ['plan', 'generate', 'apply', 'verify', 'release'];
|
|
@@ -19,6 +23,8 @@ const STUDIO_EVENT_API_VERSION = 'sce.studio.event/v0.1';
|
|
|
19
23
|
const VERIFY_PROFILES = new Set(['fast', 'standard', 'strict']);
|
|
20
24
|
const RELEASE_PROFILES = new Set(['standard', 'strict']);
|
|
21
25
|
const STUDIO_REPORTS_DIR = '.sce/reports/studio';
|
|
26
|
+
const DEFAULT_INTERACTIVE_GOVERNANCE_REPORT = '.sce/reports/interactive-governance-report.json';
|
|
27
|
+
const DEFAULT_PROBLEM_CONTRACT_RELATIVE_PATH = path.join('custom', 'problem-contract.json');
|
|
22
28
|
const MAX_OUTPUT_PREVIEW_LENGTH = 2000;
|
|
23
29
|
const DEFAULT_STUDIO_SECURITY_POLICY = Object.freeze({
|
|
24
30
|
enabled: false,
|
|
@@ -368,6 +374,7 @@ async function buildVerifyGateSteps(options = {}, dependencies = {}) {
|
|
|
368
374
|
const projectPath = dependencies.projectPath || process.cwd();
|
|
369
375
|
const fileSystem = dependencies.fileSystem || fs;
|
|
370
376
|
const profile = normalizeString(options.profile) || 'standard';
|
|
377
|
+
const specId = normalizeString(options.specId || dependencies.specId);
|
|
371
378
|
|
|
372
379
|
if (!VERIFY_PROFILES.has(profile)) {
|
|
373
380
|
throw new Error(`Invalid verify profile "${profile}". Expected one of: ${Array.from(VERIFY_PROFILES).join(', ')}`);
|
|
@@ -403,6 +410,21 @@ async function buildVerifyGateSteps(options = {}, dependencies = {}) {
|
|
|
403
410
|
}
|
|
404
411
|
|
|
405
412
|
if (profile === 'standard' || profile === 'strict') {
|
|
413
|
+
const problemClosureGateScript = path.join(projectPath, 'scripts', 'problem-closure-gate.js');
|
|
414
|
+
const hasProblemClosureGateScript = await fileSystem.pathExists(problemClosureGateScript);
|
|
415
|
+
const canRunProblemClosureGate = hasProblemClosureGateScript && Boolean(specId);
|
|
416
|
+
steps.push({
|
|
417
|
+
id: 'problem-closure-gate',
|
|
418
|
+
name: 'problem closure gate (verify)',
|
|
419
|
+
command: 'node',
|
|
420
|
+
args: ['scripts/problem-closure-gate.js', '--stage', 'verify', '--spec', specId, '--fail-on-block', '--json'],
|
|
421
|
+
required: Boolean(specId),
|
|
422
|
+
enabled: canRunProblemClosureGate,
|
|
423
|
+
skip_reason: canRunProblemClosureGate
|
|
424
|
+
? ''
|
|
425
|
+
: (specId ? 'scripts/problem-closure-gate.js not found' : 'spec id unavailable for problem-closure gate')
|
|
426
|
+
});
|
|
427
|
+
|
|
406
428
|
const governanceScript = path.join(projectPath, 'scripts', 'interactive-governance-report.js');
|
|
407
429
|
const hasGovernanceScript = await fileSystem.pathExists(governanceScript);
|
|
408
430
|
steps.push({
|
|
@@ -435,11 +457,32 @@ async function buildReleaseGateSteps(options = {}, dependencies = {}) {
|
|
|
435
457
|
const projectPath = dependencies.projectPath || process.cwd();
|
|
436
458
|
const fileSystem = dependencies.fileSystem || fs;
|
|
437
459
|
const profile = normalizeString(options.profile) || 'standard';
|
|
460
|
+
const specId = normalizeString(options.specId || dependencies.specId);
|
|
461
|
+
const verifyReportPath = normalizeString(options.verifyReportPath || dependencies.verifyReportPath);
|
|
438
462
|
if (!RELEASE_PROFILES.has(profile)) {
|
|
439
463
|
throw new Error(`Invalid release profile "${profile}". Expected one of: ${Array.from(RELEASE_PROFILES).join(', ')}`);
|
|
440
464
|
}
|
|
441
465
|
|
|
442
466
|
const steps = [];
|
|
467
|
+
const problemClosureGateScript = path.join(projectPath, 'scripts', 'problem-closure-gate.js');
|
|
468
|
+
const hasProblemClosureGateScript = await fileSystem.pathExists(problemClosureGateScript);
|
|
469
|
+
const canRunProblemClosureGate = hasProblemClosureGateScript && Boolean(specId);
|
|
470
|
+
const problemClosureArgs = ['scripts/problem-closure-gate.js', '--stage', 'release', '--spec', specId, '--fail-on-block', '--json'];
|
|
471
|
+
if (verifyReportPath) {
|
|
472
|
+
problemClosureArgs.push('--verify-report', verifyReportPath);
|
|
473
|
+
}
|
|
474
|
+
steps.push({
|
|
475
|
+
id: 'problem-closure-gate',
|
|
476
|
+
name: 'problem closure gate (release)',
|
|
477
|
+
command: 'node',
|
|
478
|
+
args: problemClosureArgs,
|
|
479
|
+
required: Boolean(specId),
|
|
480
|
+
enabled: canRunProblemClosureGate,
|
|
481
|
+
skip_reason: canRunProblemClosureGate
|
|
482
|
+
? ''
|
|
483
|
+
: (specId ? 'scripts/problem-closure-gate.js not found' : 'spec id unavailable for problem-closure gate')
|
|
484
|
+
});
|
|
485
|
+
|
|
443
486
|
steps.push({
|
|
444
487
|
id: 'npm-pack-dry-run',
|
|
445
488
|
name: 'npm pack --dry-run',
|
|
@@ -710,6 +753,77 @@ async function readSpecDomainChain(projectPath, specId, fileSystem = fs) {
|
|
|
710
753
|
}
|
|
711
754
|
}
|
|
712
755
|
|
|
756
|
+
async function readSpecProblemContract(projectPath, specId, fileSystem = fs) {
|
|
757
|
+
const specRoot = path.join(projectPath, '.sce', 'specs', specId);
|
|
758
|
+
const contractPath = path.join(specRoot, DEFAULT_PROBLEM_CONTRACT_RELATIVE_PATH);
|
|
759
|
+
if (!await fileSystem.pathExists(contractPath)) {
|
|
760
|
+
return null;
|
|
761
|
+
}
|
|
762
|
+
try {
|
|
763
|
+
const payload = await fileSystem.readJson(contractPath);
|
|
764
|
+
const stat = await fileSystem.stat(contractPath);
|
|
765
|
+
return {
|
|
766
|
+
spec_id: specId,
|
|
767
|
+
contract_path: toRelativePosix(projectPath, contractPath),
|
|
768
|
+
payload,
|
|
769
|
+
updated_at: stat && stat.mtime ? stat.mtime.toISOString() : null,
|
|
770
|
+
mtime_ms: Number(stat && stat.mtimeMs) || 0
|
|
771
|
+
};
|
|
772
|
+
} catch (_error) {
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
async function readGovernanceSignals(projectPath, fileSystem = fs) {
|
|
778
|
+
const reportPath = path.join(projectPath, DEFAULT_INTERACTIVE_GOVERNANCE_REPORT);
|
|
779
|
+
if (!await fileSystem.pathExists(reportPath)) {
|
|
780
|
+
return {
|
|
781
|
+
available: false,
|
|
782
|
+
report_path: null,
|
|
783
|
+
high_breach_count: 0,
|
|
784
|
+
medium_breach_count: 0
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
const payload = await fileSystem.readJson(reportPath).catch(() => null);
|
|
788
|
+
const summary = extractGovernanceBreachSignals(payload || {});
|
|
789
|
+
return {
|
|
790
|
+
...summary,
|
|
791
|
+
report_path: toRelativePosix(projectPath, reportPath)
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
async function readVerifyReportSignals(projectPath, verifyReportPath = '', fileSystem = fs) {
|
|
796
|
+
const normalized = normalizeString(verifyReportPath);
|
|
797
|
+
if (!normalized) {
|
|
798
|
+
return {
|
|
799
|
+
available: false,
|
|
800
|
+
report_path: null,
|
|
801
|
+
passed: false,
|
|
802
|
+
failed_step_count: 0
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
const absolutePath = path.isAbsolute(normalized)
|
|
806
|
+
? normalized
|
|
807
|
+
: path.join(projectPath, normalized);
|
|
808
|
+
if (!await fileSystem.pathExists(absolutePath)) {
|
|
809
|
+
return {
|
|
810
|
+
available: false,
|
|
811
|
+
report_path: normalized,
|
|
812
|
+
passed: false,
|
|
813
|
+
failed_step_count: 0
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
const payload = await fileSystem.readJson(absolutePath).catch(() => null);
|
|
817
|
+
const steps = Array.isArray(payload && payload.steps) ? payload.steps : [];
|
|
818
|
+
const failedStepCount = steps.filter((step) => normalizeString(step && step.status) === 'failed').length;
|
|
819
|
+
return {
|
|
820
|
+
available: true,
|
|
821
|
+
report_path: toRelativePosix(projectPath, absolutePath),
|
|
822
|
+
passed: payload && payload.passed === true && failedStepCount === 0,
|
|
823
|
+
failed_step_count: failedStepCount
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
713
827
|
function normalizeChainList(value, limit = 5) {
|
|
714
828
|
if (!Array.isArray(value)) {
|
|
715
829
|
return [];
|
|
@@ -725,13 +839,97 @@ function normalizeChainList(value, limit = 5) {
|
|
|
725
839
|
});
|
|
726
840
|
}
|
|
727
841
|
|
|
842
|
+
function normalizeProblemContract(contract = {}, context = {}) {
|
|
843
|
+
const source = contract && typeof contract === 'object' ? contract : {};
|
|
844
|
+
const issueStatement = normalizeString(
|
|
845
|
+
source.issue_statement
|
|
846
|
+
|| source.issue
|
|
847
|
+
|| source.problem_statement
|
|
848
|
+
|| context.problem_statement
|
|
849
|
+
|| context.goal
|
|
850
|
+
);
|
|
851
|
+
const expectedOutcome = normalizeString(
|
|
852
|
+
source.expected_outcome
|
|
853
|
+
|| source.expected
|
|
854
|
+
|| source.success_criteria
|
|
855
|
+
|| context.verification_plan
|
|
856
|
+
|| (context.scene_id ? `Scene ${context.scene_id} reaches deterministic verification gates.` : '')
|
|
857
|
+
);
|
|
858
|
+
const reproductionSteps = normalizeChainList(
|
|
859
|
+
source.reproduction_steps || source.repro_steps || source.steps,
|
|
860
|
+
20
|
|
861
|
+
);
|
|
862
|
+
const fallbackRepro = reproductionSteps.length > 0
|
|
863
|
+
? reproductionSteps
|
|
864
|
+
: [
|
|
865
|
+
normalizeString(context.goal) || 'Reproduce the reported issue in the target scene.',
|
|
866
|
+
'Capture logs and gate evidence for the failing path.'
|
|
867
|
+
].filter(Boolean);
|
|
868
|
+
const forbiddenWorkarounds = normalizeChainList(
|
|
869
|
+
source.forbidden_workarounds || source.prohibited_workarounds || source.disallowed_workarounds,
|
|
870
|
+
20
|
|
871
|
+
);
|
|
872
|
+
const fallbackForbidden = forbiddenWorkarounds.length > 0
|
|
873
|
+
? forbiddenWorkarounds
|
|
874
|
+
: [
|
|
875
|
+
'Do not bypass gates or tests.',
|
|
876
|
+
'Do not silence runtime errors.'
|
|
877
|
+
];
|
|
878
|
+
|
|
879
|
+
return {
|
|
880
|
+
issue_statement: issueStatement,
|
|
881
|
+
expected_outcome: expectedOutcome,
|
|
882
|
+
reproduction_steps: fallbackRepro,
|
|
883
|
+
impact_scope: normalizeString(source.impact_scope || source.scope || context.scene_id),
|
|
884
|
+
forbidden_workarounds: fallbackForbidden
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function extractGovernanceBreachSignals(report = {}) {
|
|
889
|
+
if (!report || typeof report !== 'object') {
|
|
890
|
+
return {
|
|
891
|
+
available: false,
|
|
892
|
+
high_breach_count: 0,
|
|
893
|
+
medium_breach_count: 0
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
const alerts = Array.isArray(report.alerts) ? report.alerts : [];
|
|
897
|
+
let highBreachCount = 0;
|
|
898
|
+
let mediumBreachCount = 0;
|
|
899
|
+
for (const alert of alerts) {
|
|
900
|
+
const status = normalizeString(alert && alert.status).toLowerCase();
|
|
901
|
+
const severity = normalizeString(alert && alert.severity).toLowerCase();
|
|
902
|
+
if (status !== 'breach') {
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
905
|
+
if (severity === 'high') {
|
|
906
|
+
highBreachCount += 1;
|
|
907
|
+
} else if (severity === 'medium') {
|
|
908
|
+
mediumBreachCount += 1;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return {
|
|
912
|
+
available: true,
|
|
913
|
+
high_breach_count: highBreachCount,
|
|
914
|
+
medium_breach_count: mediumBreachCount
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
|
|
728
918
|
function summarizeDomainChain(payload = {}) {
|
|
729
919
|
const ontology = payload && typeof payload.ontology === 'object' ? payload.ontology : {};
|
|
920
|
+
const ontologyEvidence = payload && typeof payload.ontology_evidence === 'object' ? payload.ontology_evidence : {};
|
|
730
921
|
const decisionPath = Array.isArray(payload.decision_execution_path) ? payload.decision_execution_path : [];
|
|
731
922
|
const correctionLoop = payload && typeof payload.correction_loop === 'object' ? payload.correction_loop : {};
|
|
732
923
|
const verification = payload && typeof payload.verification === 'object' ? payload.verification : {};
|
|
733
924
|
const hypotheses = Array.isArray(payload.hypotheses) ? payload.hypotheses : [];
|
|
734
925
|
const risks = Array.isArray(payload.risks) ? payload.risks : [];
|
|
926
|
+
const evidenceBindingCount = (
|
|
927
|
+
normalizeChainList(ontologyEvidence.entity, 50).length
|
|
928
|
+
+ normalizeChainList(ontologyEvidence.relation, 50).length
|
|
929
|
+
+ normalizeChainList(ontologyEvidence.business_rule, 50).length
|
|
930
|
+
+ normalizeChainList(ontologyEvidence.decision_policy, 50).length
|
|
931
|
+
+ normalizeChainList(ontologyEvidence.execution_flow, 50).length
|
|
932
|
+
);
|
|
735
933
|
|
|
736
934
|
return {
|
|
737
935
|
scene_id: normalizeString(payload.scene_id) || null,
|
|
@@ -747,6 +945,8 @@ function summarizeDomainChain(payload = {}) {
|
|
|
747
945
|
hypothesis_count: hypotheses.length,
|
|
748
946
|
risk_count: risks.length,
|
|
749
947
|
decision_path_steps: decisionPath.length,
|
|
948
|
+
evidence_binding_count: evidenceBindingCount,
|
|
949
|
+
verification_plan: normalizeString(verification.plan) || null,
|
|
750
950
|
correction_loop: {
|
|
751
951
|
triggers: normalizeChainList(correctionLoop.triggers, 5),
|
|
752
952
|
actions: normalizeChainList(correctionLoop.actions, 5)
|
|
@@ -781,7 +981,13 @@ function buildDomainChainRuntimeContext(payload = {}) {
|
|
|
781
981
|
verification: {
|
|
782
982
|
plan: normalizeString(payload?.verification?.plan) || null,
|
|
783
983
|
gates: normalizeChainList(payload?.verification?.gates, 10)
|
|
784
|
-
}
|
|
984
|
+
},
|
|
985
|
+
problem_contract: normalizeProblemContract(payload?.problem_contract || {}, {
|
|
986
|
+
scene_id: normalizeString(payload.scene_id) || '',
|
|
987
|
+
goal: normalizeString(payload?.problem?.statement) || '',
|
|
988
|
+
problem_statement: normalizeString(payload?.problem?.statement) || '',
|
|
989
|
+
verification_plan: normalizeString(payload?.verification?.plan) || ''
|
|
990
|
+
})
|
|
785
991
|
};
|
|
786
992
|
}
|
|
787
993
|
|
|
@@ -834,12 +1040,22 @@ async function resolveDomainChainBinding(options = {}, dependencies = {}) {
|
|
|
834
1040
|
problemStatement: goal || `Studio scene cycle for ${sceneId}`
|
|
835
1041
|
});
|
|
836
1042
|
const chain = await readSpecDomainChain(projectPath, explicitSpec, fileSystem);
|
|
1043
|
+
const problemContract = await readSpecProblemContract(projectPath, explicitSpec, fileSystem);
|
|
837
1044
|
if (!chain) {
|
|
838
1045
|
return {
|
|
839
1046
|
resolved: false,
|
|
840
1047
|
source: 'explicit-spec',
|
|
841
1048
|
spec_id: explicitSpec,
|
|
842
|
-
reason: 'domain_chain_missing'
|
|
1049
|
+
reason: 'domain_chain_missing',
|
|
1050
|
+
problem_contract: problemContract
|
|
1051
|
+
? normalizeProblemContract(problemContract.payload, {
|
|
1052
|
+
scene_id: sceneId,
|
|
1053
|
+
goal
|
|
1054
|
+
})
|
|
1055
|
+
: normalizeProblemContract({}, {
|
|
1056
|
+
scene_id: sceneId,
|
|
1057
|
+
goal
|
|
1058
|
+
})
|
|
843
1059
|
};
|
|
844
1060
|
}
|
|
845
1061
|
return {
|
|
@@ -849,7 +1065,17 @@ async function resolveDomainChainBinding(options = {}, dependencies = {}) {
|
|
|
849
1065
|
chain_path: chain.chain_path,
|
|
850
1066
|
updated_at: chain.updated_at,
|
|
851
1067
|
summary: summarizeDomainChain(chain.payload),
|
|
852
|
-
context: buildDomainChainRuntimeContext(chain.payload)
|
|
1068
|
+
context: buildDomainChainRuntimeContext(chain.payload),
|
|
1069
|
+
problem_contract: normalizeProblemContract(
|
|
1070
|
+
problemContract && problemContract.payload ? problemContract.payload : chain.payload?.problem_contract || {},
|
|
1071
|
+
{
|
|
1072
|
+
scene_id: sceneId,
|
|
1073
|
+
goal,
|
|
1074
|
+
problem_statement: normalizeString(chain?.payload?.problem?.statement),
|
|
1075
|
+
verification_plan: normalizeString(chain?.payload?.verification?.plan)
|
|
1076
|
+
}
|
|
1077
|
+
),
|
|
1078
|
+
problem_contract_path: problemContract ? problemContract.contract_path : null
|
|
853
1079
|
};
|
|
854
1080
|
}
|
|
855
1081
|
|
|
@@ -873,6 +1099,7 @@ async function resolveDomainChainBinding(options = {}, dependencies = {}) {
|
|
|
873
1099
|
}
|
|
874
1100
|
|
|
875
1101
|
const selected = candidates[0];
|
|
1102
|
+
const selectedContract = await readSpecProblemContract(projectPath, selected.spec_id, fileSystem);
|
|
876
1103
|
return {
|
|
877
1104
|
resolved: true,
|
|
878
1105
|
source: candidates.length === 1 ? 'scene-auto-single' : 'scene-auto-latest',
|
|
@@ -886,7 +1113,17 @@ async function resolveDomainChainBinding(options = {}, dependencies = {}) {
|
|
|
886
1113
|
updated_at: item.updated_at
|
|
887
1114
|
})),
|
|
888
1115
|
summary: summarizeDomainChain(selected.payload),
|
|
889
|
-
context: buildDomainChainRuntimeContext(selected.payload)
|
|
1116
|
+
context: buildDomainChainRuntimeContext(selected.payload),
|
|
1117
|
+
problem_contract: normalizeProblemContract(
|
|
1118
|
+
selectedContract && selectedContract.payload ? selectedContract.payload : selected.payload?.problem_contract || {},
|
|
1119
|
+
{
|
|
1120
|
+
scene_id: sceneId,
|
|
1121
|
+
goal,
|
|
1122
|
+
problem_statement: normalizeString(selected?.payload?.problem?.statement),
|
|
1123
|
+
verification_plan: normalizeString(selected?.payload?.verification?.plan)
|
|
1124
|
+
}
|
|
1125
|
+
),
|
|
1126
|
+
problem_contract_path: selectedContract ? selectedContract.contract_path : null
|
|
890
1127
|
};
|
|
891
1128
|
}
|
|
892
1129
|
|
|
@@ -949,6 +1186,14 @@ function buildJobDomainChainMetadata(job = {}) {
|
|
|
949
1186
|
: null;
|
|
950
1187
|
const summary = domainChain && domainChain.summary ? domainChain.summary : null;
|
|
951
1188
|
const context = domainChain && domainChain.context ? domainChain.context : null;
|
|
1189
|
+
const problemContract = job?.source?.problem_contract && typeof job.source.problem_contract === 'object'
|
|
1190
|
+
? job.source.problem_contract
|
|
1191
|
+
: normalizeProblemContract(context && context.problem_contract ? context.problem_contract : {}, {
|
|
1192
|
+
scene_id: normalizeString(job?.scene?.id),
|
|
1193
|
+
goal: normalizeString(job?.source?.goal),
|
|
1194
|
+
problem_statement: normalizeString(summary && summary.problem_statement),
|
|
1195
|
+
verification_plan: normalizeString(summary && summary.verification_plan)
|
|
1196
|
+
});
|
|
952
1197
|
return {
|
|
953
1198
|
resolved: domainChain && domainChain.resolved === true,
|
|
954
1199
|
source: domainChain && domainChain.source ? domainChain.source : 'none',
|
|
@@ -957,12 +1202,14 @@ function buildJobDomainChainMetadata(job = {}) {
|
|
|
957
1202
|
reason: domainChain && domainChain.reason ? domainChain.reason : null,
|
|
958
1203
|
decision_path_steps: summary ? Number(summary.decision_path_steps || 0) : 0,
|
|
959
1204
|
risk_count: summary ? Number(summary.risk_count || 0) : 0,
|
|
1205
|
+
evidence_binding_count: summary ? Number(summary.evidence_binding_count || 0) : 0,
|
|
960
1206
|
correction_triggers: summary && summary.correction_loop
|
|
961
1207
|
? normalizeChainList(summary.correction_loop.triggers, 10)
|
|
962
1208
|
: [],
|
|
963
1209
|
verification_gates: summary
|
|
964
1210
|
? normalizeChainList(summary.verification_gates, 10)
|
|
965
1211
|
: [],
|
|
1212
|
+
problem_contract: problemContract,
|
|
966
1213
|
summary: summary || null,
|
|
967
1214
|
context: context || null
|
|
968
1215
|
};
|
|
@@ -975,6 +1222,18 @@ function summarizeProblemEvaluation(evaluation = {}) {
|
|
|
975
1222
|
confidence_score: Number(evaluation.confidence_score || 0),
|
|
976
1223
|
risk_level: normalizeString(evaluation?.dimensions?.risk?.level) || 'low',
|
|
977
1224
|
strategy: normalizeString(evaluation?.dimensions?.strategy?.strategy) || 'direct-execution',
|
|
1225
|
+
contract_score: Number(evaluation?.dimensions?.problem_contract?.score || 0),
|
|
1226
|
+
ontology_score: Number(evaluation?.dimensions?.ontology_alignment?.score || 0),
|
|
1227
|
+
convergence_score: Number(evaluation?.dimensions?.convergence?.score || 0),
|
|
1228
|
+
contract_missing: Array.isArray(evaluation?.dimensions?.problem_contract?.missing)
|
|
1229
|
+
? evaluation.dimensions.problem_contract.missing
|
|
1230
|
+
: [],
|
|
1231
|
+
ontology_missing_axes: Array.isArray(evaluation?.dimensions?.ontology_alignment?.missing_axes)
|
|
1232
|
+
? evaluation.dimensions.ontology_alignment.missing_axes
|
|
1233
|
+
: [],
|
|
1234
|
+
convergence_missing: Array.isArray(evaluation?.dimensions?.convergence?.missing)
|
|
1235
|
+
? evaluation.dimensions.convergence.missing
|
|
1236
|
+
: [],
|
|
978
1237
|
blockers: Array.isArray(evaluation.blockers) ? evaluation.blockers : [],
|
|
979
1238
|
warnings: Array.isArray(evaluation.warnings) ? evaluation.warnings : [],
|
|
980
1239
|
recommendations: Array.isArray(evaluation.recommendations) ? evaluation.recommendations : [],
|
|
@@ -1044,6 +1303,7 @@ async function enforceProblemEvaluationForStage(job = {}, stage = '', context =
|
|
|
1044
1303
|
goal: normalizeString(context.goal || job?.source?.goal),
|
|
1045
1304
|
release_channel: normalizeString(context.release_channel || ''),
|
|
1046
1305
|
domain_chain: context.domain_chain || (job?.source?.domain_chain || {}),
|
|
1306
|
+
problem_contract: context.problem_contract || job?.source?.problem_contract || {},
|
|
1047
1307
|
related_specs_count: Number(context.related_specs_count || job?.source?.related_specs?.total_candidates || 0),
|
|
1048
1308
|
stage_readiness: context.stage_readiness || buildStageReadiness(job, stage),
|
|
1049
1309
|
gate_signals: context.gate_signals || {}
|
|
@@ -1087,6 +1347,9 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1087
1347
|
const fromChat = normalizeString(options.fromChat);
|
|
1088
1348
|
const sceneId = normalizeString(options.scene);
|
|
1089
1349
|
const specId = normalizeString(options.spec);
|
|
1350
|
+
const goal = normalizeString(options.goal);
|
|
1351
|
+
const manualSpecMode = options.manualSpec === true;
|
|
1352
|
+
const skipSpecGovernance = options.specGovernance === false;
|
|
1090
1353
|
|
|
1091
1354
|
if (!fromChat) {
|
|
1092
1355
|
throw new Error('--from-chat is required');
|
|
@@ -1094,16 +1357,18 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1094
1357
|
if (!sceneId) {
|
|
1095
1358
|
throw new Error('--scene is required');
|
|
1096
1359
|
}
|
|
1097
|
-
|
|
1360
|
+
|
|
1361
|
+
let domainChainBinding = await resolveDomainChainBinding({
|
|
1098
1362
|
sceneId,
|
|
1099
1363
|
specId,
|
|
1100
|
-
goal
|
|
1364
|
+
goal
|
|
1101
1365
|
}, {
|
|
1102
1366
|
projectPath,
|
|
1103
1367
|
fileSystem
|
|
1104
1368
|
});
|
|
1105
|
-
|
|
1106
|
-
|
|
1369
|
+
|
|
1370
|
+
let relatedSpecLookup = await findRelatedSpecs({
|
|
1371
|
+
query: goal,
|
|
1107
1372
|
sceneId,
|
|
1108
1373
|
limit: 8,
|
|
1109
1374
|
excludeSpecId: domainChainBinding.spec_id || specId || null
|
|
@@ -1111,6 +1376,45 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1111
1376
|
projectPath,
|
|
1112
1377
|
fileSystem
|
|
1113
1378
|
});
|
|
1379
|
+
|
|
1380
|
+
const intake = await runStudioAutoIntake({
|
|
1381
|
+
scene_id: sceneId,
|
|
1382
|
+
from_chat: fromChat,
|
|
1383
|
+
goal,
|
|
1384
|
+
explicit_spec_id: specId,
|
|
1385
|
+
domain_chain_binding: domainChainBinding,
|
|
1386
|
+
related_specs: relatedSpecLookup,
|
|
1387
|
+
apply: !manualSpecMode,
|
|
1388
|
+
skip: manualSpecMode
|
|
1389
|
+
}, {
|
|
1390
|
+
projectPath,
|
|
1391
|
+
fileSystem
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1394
|
+
const intakeSpecId = normalizeString(intake && intake.selected_spec_id);
|
|
1395
|
+
const effectiveSpecId = intakeSpecId || normalizeString(domainChainBinding.spec_id) || specId || null;
|
|
1396
|
+
|
|
1397
|
+
if (effectiveSpecId && effectiveSpecId !== normalizeString(domainChainBinding.spec_id)) {
|
|
1398
|
+
domainChainBinding = await resolveDomainChainBinding({
|
|
1399
|
+
sceneId,
|
|
1400
|
+
specId: effectiveSpecId,
|
|
1401
|
+
goal
|
|
1402
|
+
}, {
|
|
1403
|
+
projectPath,
|
|
1404
|
+
fileSystem
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
relatedSpecLookup = await findRelatedSpecs({
|
|
1409
|
+
query: goal,
|
|
1410
|
+
sceneId,
|
|
1411
|
+
limit: 8,
|
|
1412
|
+
excludeSpecId: effectiveSpecId || null
|
|
1413
|
+
}, {
|
|
1414
|
+
projectPath,
|
|
1415
|
+
fileSystem
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1114
1418
|
const relatedSpecItems = Array.isArray(relatedSpecLookup.related_specs)
|
|
1115
1419
|
? relatedSpecLookup.related_specs.map((item) => ({
|
|
1116
1420
|
spec_id: item.spec_id,
|
|
@@ -1127,15 +1431,26 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1127
1431
|
|
|
1128
1432
|
const jobId = normalizeString(options.job) || createJobId();
|
|
1129
1433
|
const now = nowIso();
|
|
1434
|
+
const problemContract = normalizeProblemContract(
|
|
1435
|
+
domainChainBinding.problem_contract || {},
|
|
1436
|
+
{
|
|
1437
|
+
scene_id: sceneId,
|
|
1438
|
+
goal,
|
|
1439
|
+
problem_statement: normalizeString(domainChainBinding?.summary?.problem_statement),
|
|
1440
|
+
verification_plan: normalizeString(domainChainBinding?.summary?.verification_plan)
|
|
1441
|
+
}
|
|
1442
|
+
);
|
|
1130
1443
|
const planShadowJob = {
|
|
1131
1444
|
job_id: jobId,
|
|
1132
1445
|
scene: {
|
|
1133
1446
|
id: sceneId,
|
|
1134
|
-
spec_id:
|
|
1447
|
+
spec_id: effectiveSpecId
|
|
1135
1448
|
},
|
|
1136
1449
|
source: {
|
|
1137
|
-
goal:
|
|
1138
|
-
spec_id:
|
|
1450
|
+
goal: goal || null,
|
|
1451
|
+
spec_id: effectiveSpecId,
|
|
1452
|
+
problem_contract: problemContract,
|
|
1453
|
+
problem_contract_path: domainChainBinding.problem_contract_path || null,
|
|
1139
1454
|
domain_chain: {
|
|
1140
1455
|
resolved: domainChainBinding.resolved === true,
|
|
1141
1456
|
summary: domainChainBinding.summary || null
|
|
@@ -1148,8 +1463,9 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1148
1463
|
};
|
|
1149
1464
|
const planProblemEvaluation = await enforceProblemEvaluationForStage(planShadowJob, 'plan', {
|
|
1150
1465
|
scene_id: sceneId,
|
|
1151
|
-
spec_id:
|
|
1152
|
-
goal:
|
|
1466
|
+
spec_id: effectiveSpecId,
|
|
1467
|
+
goal: goal || null,
|
|
1468
|
+
problem_contract: problemContract,
|
|
1153
1469
|
domain_chain: {
|
|
1154
1470
|
resolved: domainChainBinding.resolved === true,
|
|
1155
1471
|
summary: domainChainBinding.summary || null
|
|
@@ -1176,16 +1492,35 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1176
1492
|
const sessionStore = dependencies.sessionStore || new SessionStore(projectPath);
|
|
1177
1493
|
const sceneSessionBinding = await sessionStore.beginSceneSession({
|
|
1178
1494
|
sceneId,
|
|
1179
|
-
objective:
|
|
1495
|
+
objective: goal || `Studio scene cycle for ${sceneId}`,
|
|
1180
1496
|
tool: normalizeString(options.tool) || 'generic'
|
|
1181
1497
|
});
|
|
1498
|
+
|
|
1499
|
+
let governanceSnapshot = null;
|
|
1500
|
+
let governanceWarning = '';
|
|
1501
|
+
const autoRunGovernance = !(skipSpecGovernance)
|
|
1502
|
+
&& (!intake || !intake.policy || !intake.policy.governance || intake.policy.governance.auto_run_on_plan !== false);
|
|
1503
|
+
if (autoRunGovernance) {
|
|
1504
|
+
try {
|
|
1505
|
+
governanceSnapshot = await runStudioSpecGovernance({
|
|
1506
|
+
apply: true,
|
|
1507
|
+
scene: sceneId
|
|
1508
|
+
}, {
|
|
1509
|
+
projectPath,
|
|
1510
|
+
fileSystem
|
|
1511
|
+
});
|
|
1512
|
+
} catch (error) {
|
|
1513
|
+
governanceWarning = normalizeString(error && error.message);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1182
1517
|
stages.plan = {
|
|
1183
1518
|
status: 'completed',
|
|
1184
1519
|
completed_at: now,
|
|
1185
1520
|
metadata: {
|
|
1186
1521
|
from_chat: fromChat,
|
|
1187
1522
|
scene_id: sceneId,
|
|
1188
|
-
spec_id:
|
|
1523
|
+
spec_id: effectiveSpecId,
|
|
1189
1524
|
scene_session_id: sceneSessionBinding.session.session_id,
|
|
1190
1525
|
scene_cycle: sceneSessionBinding.scene_cycle,
|
|
1191
1526
|
domain_chain_resolved: domainChainBinding.resolved === true,
|
|
@@ -1194,7 +1529,19 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1194
1529
|
domain_chain_path: domainChainBinding.chain_path || null,
|
|
1195
1530
|
domain_chain_summary: domainChainBinding.summary || null,
|
|
1196
1531
|
domain_chain_reason: domainChainBinding.reason || null,
|
|
1532
|
+
problem_contract: problemContract,
|
|
1533
|
+
intake: intake ? {
|
|
1534
|
+
enabled: intake.enabled === true,
|
|
1535
|
+
intent_type: intake.intent ? intake.intent.intent_type : null,
|
|
1536
|
+
decision_action: intake.decision ? intake.decision.action : null,
|
|
1537
|
+
decision_reason: intake.decision ? intake.decision.reason : null,
|
|
1538
|
+
selected_spec_id: intake.selected_spec_id || effectiveSpecId || null,
|
|
1539
|
+
created_spec_id: intake.created_spec && intake.created_spec.created ? intake.created_spec.spec_id : null,
|
|
1540
|
+
policy_path: intake.policy_path || null
|
|
1541
|
+
} : null,
|
|
1197
1542
|
problem_evaluation: summarizeProblemEvaluation(planProblemEvaluation),
|
|
1543
|
+
spec_governance: governanceSnapshot ? governanceSnapshot.summary : null,
|
|
1544
|
+
spec_governance_warning: governanceWarning || null,
|
|
1198
1545
|
related_specs_total: Number(relatedSpecLookup.total_candidates || 0),
|
|
1199
1546
|
related_specs_top: relatedSpecItems
|
|
1200
1547
|
}
|
|
@@ -1208,13 +1555,24 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1208
1555
|
status: 'planned',
|
|
1209
1556
|
source: {
|
|
1210
1557
|
from_chat: fromChat,
|
|
1211
|
-
goal:
|
|
1212
|
-
spec_id:
|
|
1558
|
+
goal: goal || null,
|
|
1559
|
+
spec_id: effectiveSpecId,
|
|
1560
|
+
problem_contract: problemContract,
|
|
1561
|
+
problem_contract_path: domainChainBinding.problem_contract_path || null,
|
|
1562
|
+
intake: intake ? {
|
|
1563
|
+
enabled: intake.enabled === true,
|
|
1564
|
+
policy_path: intake.policy_path || null,
|
|
1565
|
+
policy_loaded_from: intake.policy_loaded_from || null,
|
|
1566
|
+
intent: intake.intent || null,
|
|
1567
|
+
decision: intake.decision || null,
|
|
1568
|
+
selected_spec_id: intake.selected_spec_id || effectiveSpecId || null,
|
|
1569
|
+
created_spec: intake.created_spec || null
|
|
1570
|
+
} : null,
|
|
1213
1571
|
domain_chain: {
|
|
1214
1572
|
resolved: domainChainBinding.resolved === true,
|
|
1215
1573
|
source: domainChainBinding.source || 'none',
|
|
1216
1574
|
reason: domainChainBinding.reason || null,
|
|
1217
|
-
spec_id: domainChainBinding.spec_id || null,
|
|
1575
|
+
spec_id: effectiveSpecId || domainChainBinding.spec_id || null,
|
|
1218
1576
|
chain_path: domainChainBinding.chain_path || null,
|
|
1219
1577
|
candidate_count: Number.isFinite(Number(domainChainBinding.candidate_count))
|
|
1220
1578
|
? Number(domainChainBinding.candidate_count)
|
|
@@ -1229,11 +1587,20 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1229
1587
|
scene_id: relatedSpecLookup.scene_id || null,
|
|
1230
1588
|
total_candidates: Number(relatedSpecLookup.total_candidates || 0),
|
|
1231
1589
|
items: relatedSpecItems
|
|
1232
|
-
}
|
|
1590
|
+
},
|
|
1591
|
+
spec_governance: governanceSnapshot
|
|
1592
|
+
? {
|
|
1593
|
+
status: governanceSnapshot.summary ? governanceSnapshot.summary.status : null,
|
|
1594
|
+
alert_count: governanceSnapshot.summary ? Number(governanceSnapshot.summary.alert_count || 0) : 0,
|
|
1595
|
+
report_file: governanceSnapshot.report_file || null,
|
|
1596
|
+
scene_index_file: governanceSnapshot.scene_index_file || null
|
|
1597
|
+
}
|
|
1598
|
+
: null,
|
|
1599
|
+
spec_governance_warning: governanceWarning || null
|
|
1233
1600
|
},
|
|
1234
1601
|
scene: {
|
|
1235
1602
|
id: sceneId,
|
|
1236
|
-
spec_id:
|
|
1603
|
+
spec_id: effectiveSpecId,
|
|
1237
1604
|
related_spec_ids: relatedSpecItems.map((item) => item.spec_id)
|
|
1238
1605
|
},
|
|
1239
1606
|
session: {
|
|
@@ -1249,6 +1616,12 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1249
1616
|
patch_bundle_id: null,
|
|
1250
1617
|
verify_report: null,
|
|
1251
1618
|
release_ref: null,
|
|
1619
|
+
spec_portfolio_report: governanceSnapshot && governanceSnapshot.report_file
|
|
1620
|
+
? governanceSnapshot.report_file
|
|
1621
|
+
: null,
|
|
1622
|
+
spec_scene_index: governanceSnapshot && governanceSnapshot.scene_index_file
|
|
1623
|
+
? governanceSnapshot.scene_index_file
|
|
1624
|
+
: null,
|
|
1252
1625
|
problem_eval_reports: {
|
|
1253
1626
|
plan: normalizeString(planProblemEvaluation.report_file) || null
|
|
1254
1627
|
}
|
|
@@ -1259,7 +1632,7 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1259
1632
|
await appendStudioEvent(paths, job, 'stage.plan.completed', {
|
|
1260
1633
|
from_chat: fromChat,
|
|
1261
1634
|
scene_id: sceneId,
|
|
1262
|
-
spec_id:
|
|
1635
|
+
spec_id: effectiveSpecId,
|
|
1263
1636
|
scene_session_id: sceneSessionBinding.session.session_id,
|
|
1264
1637
|
scene_cycle: sceneSessionBinding.scene_cycle,
|
|
1265
1638
|
target: job.target,
|
|
@@ -1267,13 +1640,28 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
1267
1640
|
domain_chain_source: domainChainBinding.source || 'none',
|
|
1268
1641
|
domain_chain_spec_id: domainChainBinding.spec_id || null,
|
|
1269
1642
|
domain_chain_path: domainChainBinding.chain_path || null,
|
|
1643
|
+
problem_contract: problemContract,
|
|
1644
|
+
intake_action: intake && intake.decision ? intake.decision.action : null,
|
|
1645
|
+
intake_reason: intake && intake.decision ? intake.decision.reason : null,
|
|
1646
|
+
intake_selected_spec_id: intake ? intake.selected_spec_id || effectiveSpecId || null : effectiveSpecId,
|
|
1647
|
+
intake_created_spec_id: intake && intake.created_spec && intake.created_spec.created
|
|
1648
|
+
? intake.created_spec.spec_id
|
|
1649
|
+
: null,
|
|
1270
1650
|
problem_evaluation: summarizeProblemEvaluation(planProblemEvaluation),
|
|
1651
|
+
spec_governance: governanceSnapshot ? governanceSnapshot.summary : null,
|
|
1652
|
+
spec_governance_warning: governanceWarning || null,
|
|
1271
1653
|
related_specs_total: Number(relatedSpecLookup.total_candidates || 0),
|
|
1272
1654
|
related_spec_ids: relatedSpecItems.map((item) => item.spec_id)
|
|
1273
1655
|
}, fileSystem);
|
|
1274
1656
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
1275
1657
|
|
|
1276
1658
|
const payload = buildCommandPayload('studio-plan', job);
|
|
1659
|
+
payload.scene = {
|
|
1660
|
+
id: sceneId,
|
|
1661
|
+
spec_id: effectiveSpecId
|
|
1662
|
+
};
|
|
1663
|
+
payload.intake = job.source && job.source.intake ? job.source.intake : null;
|
|
1664
|
+
payload.spec_governance = governanceSnapshot ? governanceSnapshot.summary : null;
|
|
1277
1665
|
printStudioPayload(payload, options);
|
|
1278
1666
|
return payload;
|
|
1279
1667
|
}
|
|
@@ -1457,16 +1845,19 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
|
|
|
1457
1845
|
const autoErrorbookRecords = [];
|
|
1458
1846
|
const gateSteps = await buildVerifyGateSteps({ profile }, {
|
|
1459
1847
|
projectPath,
|
|
1460
|
-
fileSystem
|
|
1848
|
+
fileSystem,
|
|
1849
|
+
specId: normalizeString(domainChainMetadata.spec_id) || null
|
|
1461
1850
|
});
|
|
1462
1851
|
const verifyProblemEvaluation = await enforceProblemEvaluationForStage(job, 'verify', {
|
|
1463
1852
|
scene_id: normalizeString(job?.scene?.id),
|
|
1464
1853
|
spec_id: normalizeString(domainChainMetadata.spec_id) || normalizeString(job?.source?.spec_id),
|
|
1465
1854
|
goal: normalizeString(job?.source?.goal),
|
|
1466
1855
|
domain_chain: job?.source?.domain_chain || {},
|
|
1856
|
+
problem_contract: job?.source?.problem_contract || {},
|
|
1467
1857
|
related_specs_count: Number(job?.source?.related_specs?.total_candidates || 0),
|
|
1468
1858
|
stage_readiness: buildStageReadiness(job, 'verify', {
|
|
1469
|
-
gate_required_ready: deriveGateSignals(gateSteps).required_missing === 0
|
|
1859
|
+
gate_required_ready: deriveGateSignals(gateSteps).required_missing === 0,
|
|
1860
|
+
convergence_strict: profile === 'strict'
|
|
1470
1861
|
}),
|
|
1471
1862
|
gate_signals: deriveGateSignals(gateSteps)
|
|
1472
1863
|
}, {
|
|
@@ -1609,9 +2000,17 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
1609
2000
|
const releaseStartedAt = nowIso();
|
|
1610
2001
|
const domainChainMetadata = buildJobDomainChainMetadata(job);
|
|
1611
2002
|
const autoErrorbookRecords = [];
|
|
1612
|
-
const
|
|
2003
|
+
const verifyReportSignals = await readVerifyReportSignals(
|
|
1613
2004
|
projectPath,
|
|
2005
|
+
normalizeString(job?.artifacts?.verify_report),
|
|
1614
2006
|
fileSystem
|
|
2007
|
+
);
|
|
2008
|
+
const governanceSignals = await readGovernanceSignals(projectPath, fileSystem);
|
|
2009
|
+
const gateSteps = await buildReleaseGateSteps({ profile }, {
|
|
2010
|
+
projectPath,
|
|
2011
|
+
fileSystem,
|
|
2012
|
+
specId: normalizeString(domainChainMetadata.spec_id) || null,
|
|
2013
|
+
verifyReportPath: normalizeString(job?.artifacts?.verify_report) || null
|
|
1615
2014
|
});
|
|
1616
2015
|
const releaseGateSignals = deriveGateSignals(gateSteps);
|
|
1617
2016
|
const releaseProblemEvaluation = await enforceProblemEvaluationForStage(job, 'release', {
|
|
@@ -1620,9 +2019,17 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
1620
2019
|
goal: normalizeString(job?.source?.goal),
|
|
1621
2020
|
release_channel: channel,
|
|
1622
2021
|
domain_chain: job?.source?.domain_chain || {},
|
|
2022
|
+
problem_contract: job?.source?.problem_contract || {},
|
|
1623
2023
|
related_specs_count: Number(job?.source?.related_specs?.total_candidates || 0),
|
|
1624
2024
|
stage_readiness: buildStageReadiness(job, 'release', {
|
|
1625
|
-
gate_required_ready: releaseGateSignals.required_missing === 0
|
|
2025
|
+
gate_required_ready: releaseGateSignals.required_missing === 0,
|
|
2026
|
+
convergence_strict: profile === 'strict',
|
|
2027
|
+
verify_stage_passed: isStageCompleted(job, 'verify'),
|
|
2028
|
+
verify_report_ready: verifyReportSignals.available,
|
|
2029
|
+
verify_report_passed: verifyReportSignals.passed,
|
|
2030
|
+
regression_passed: verifyReportSignals.passed && verifyReportSignals.failed_step_count === 0,
|
|
2031
|
+
governance_report_ready: governanceSignals.available,
|
|
2032
|
+
high_alert_count: Number(governanceSignals.high_breach_count || 0)
|
|
1626
2033
|
}),
|
|
1627
2034
|
gate_signals: releaseGateSignals
|
|
1628
2035
|
}, {
|
|
@@ -1672,6 +2079,8 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
1672
2079
|
passed: gateResult.passed,
|
|
1673
2080
|
steps: gateResult.steps,
|
|
1674
2081
|
domain_chain: domainChainMetadata,
|
|
2082
|
+
verify_signals: verifyReportSignals,
|
|
2083
|
+
governance_signals: governanceSignals,
|
|
1675
2084
|
problem_evaluation: summarizeProblemEvaluation(releaseProblemEvaluation),
|
|
1676
2085
|
auto_errorbook_records: autoErrorbookRecords
|
|
1677
2086
|
};
|
|
@@ -1885,6 +2294,116 @@ async function runStudioEventsCommand(options = {}, dependencies = {}) {
|
|
|
1885
2294
|
return payload;
|
|
1886
2295
|
}
|
|
1887
2296
|
|
|
2297
|
+
function printStudioIntakePayload(payload, options = {}) {
|
|
2298
|
+
if (options.json) {
|
|
2299
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
console.log(chalk.blue('Studio intake'));
|
|
2304
|
+
console.log(` Scene: ${payload.scene_id || 'n/a'}`);
|
|
2305
|
+
console.log(` Goal: ${payload.goal || '(empty)'}`);
|
|
2306
|
+
console.log(` Intent: ${payload.intent && payload.intent.intent_type ? payload.intent.intent_type : 'unknown'}`);
|
|
2307
|
+
console.log(` Decision: ${payload.decision && payload.decision.action ? payload.decision.action : 'none'}`);
|
|
2308
|
+
console.log(` Spec: ${payload.selected_spec_id || 'n/a'}`);
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
async function runStudioIntakeCommand(options = {}, dependencies = {}) {
|
|
2312
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
2313
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
2314
|
+
const sceneId = normalizeString(options.scene);
|
|
2315
|
+
const fromChat = normalizeString(options.fromChat);
|
|
2316
|
+
const goal = normalizeString(options.goal);
|
|
2317
|
+
const specId = normalizeString(options.spec);
|
|
2318
|
+
|
|
2319
|
+
if (!sceneId) {
|
|
2320
|
+
throw new Error('--scene is required');
|
|
2321
|
+
}
|
|
2322
|
+
if (!fromChat) {
|
|
2323
|
+
throw new Error('--from-chat is required');
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
const domainChainBinding = await resolveDomainChainBinding({
|
|
2327
|
+
sceneId,
|
|
2328
|
+
specId,
|
|
2329
|
+
goal
|
|
2330
|
+
}, {
|
|
2331
|
+
projectPath,
|
|
2332
|
+
fileSystem
|
|
2333
|
+
});
|
|
2334
|
+
|
|
2335
|
+
const relatedSpecLookup = await findRelatedSpecs({
|
|
2336
|
+
query: goal,
|
|
2337
|
+
sceneId,
|
|
2338
|
+
limit: 8,
|
|
2339
|
+
excludeSpecId: domainChainBinding.spec_id || specId || null
|
|
2340
|
+
}, {
|
|
2341
|
+
projectPath,
|
|
2342
|
+
fileSystem
|
|
2343
|
+
});
|
|
2344
|
+
|
|
2345
|
+
const intake = await runStudioAutoIntake({
|
|
2346
|
+
scene_id: sceneId,
|
|
2347
|
+
from_chat: fromChat,
|
|
2348
|
+
goal,
|
|
2349
|
+
explicit_spec_id: specId,
|
|
2350
|
+
domain_chain_binding: domainChainBinding,
|
|
2351
|
+
related_specs: relatedSpecLookup,
|
|
2352
|
+
apply: options.apply === true,
|
|
2353
|
+
skip: options.manualSpec === true
|
|
2354
|
+
}, {
|
|
2355
|
+
projectPath,
|
|
2356
|
+
fileSystem
|
|
2357
|
+
});
|
|
2358
|
+
|
|
2359
|
+
const payload = {
|
|
2360
|
+
...intake,
|
|
2361
|
+
domain_chain_source: domainChainBinding.source || 'none',
|
|
2362
|
+
domain_chain_spec_id: domainChainBinding.spec_id || null,
|
|
2363
|
+
related_specs_total: Number(relatedSpecLookup.total_candidates || 0)
|
|
2364
|
+
};
|
|
2365
|
+
printStudioIntakePayload(payload, options);
|
|
2366
|
+
return payload;
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
function printStudioPortfolioPayload(payload, options = {}) {
|
|
2370
|
+
if (options.json) {
|
|
2371
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2374
|
+
const summary = payload.summary || {};
|
|
2375
|
+
console.log(chalk.blue('Studio portfolio governance'));
|
|
2376
|
+
console.log(` Status: ${summary.status || 'unknown'}`);
|
|
2377
|
+
console.log(` Scenes: ${summary.scene_count || 0}`);
|
|
2378
|
+
console.log(` Specs: ${summary.total_specs || 0}`);
|
|
2379
|
+
console.log(` Active: ${summary.active_specs || 0}`);
|
|
2380
|
+
console.log(` Completed: ${summary.completed_specs || 0}`);
|
|
2381
|
+
console.log(` Stale: ${summary.stale_specs || 0}`);
|
|
2382
|
+
console.log(` Duplicate pairs: ${summary.duplicate_pairs || 0}`);
|
|
2383
|
+
console.log(` Overflow scenes: ${summary.overflow_scenes || 0}`);
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
async function runStudioPortfolioCommand(options = {}, dependencies = {}) {
|
|
2387
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
2388
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
2389
|
+
const payload = await runStudioSpecGovernance({
|
|
2390
|
+
scene: normalizeString(options.scene),
|
|
2391
|
+
apply: options.apply !== false
|
|
2392
|
+
}, {
|
|
2393
|
+
projectPath,
|
|
2394
|
+
fileSystem
|
|
2395
|
+
});
|
|
2396
|
+
|
|
2397
|
+
if (options.strict && payload.summary && Number(payload.summary.alert_count || 0) > 0) {
|
|
2398
|
+
throw new Error(
|
|
2399
|
+
`studio portfolio governance has alerts: ${payload.summary.alert_count} (duplicate/stale/overflow)`
|
|
2400
|
+
);
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
printStudioPortfolioPayload(payload, options);
|
|
2404
|
+
return payload;
|
|
2405
|
+
}
|
|
2406
|
+
|
|
1888
2407
|
async function runStudioCommand(handler, options, stageName = '') {
|
|
1889
2408
|
try {
|
|
1890
2409
|
const stage = normalizeString(stageName) || 'unknown';
|
|
@@ -1928,11 +2447,34 @@ function registerStudioCommands(program) {
|
|
|
1928
2447
|
.requiredOption('--from-chat <session>', 'Chat session identifier or transcript reference')
|
|
1929
2448
|
.option('--spec <spec-id>', 'Optional spec binding for domain-chain context ingestion')
|
|
1930
2449
|
.option('--goal <goal>', 'Optional goal summary')
|
|
2450
|
+
.option('--manual-spec', 'Disable auto intake and only use explicit --spec or existing scene binding')
|
|
1931
2451
|
.option('--target <target>', 'Target integration profile', 'default')
|
|
2452
|
+
.option('--no-spec-governance', 'Skip auto portfolio governance snapshot on plan stage')
|
|
1932
2453
|
.option('--job <job-id>', 'Reuse an explicit studio job id')
|
|
1933
2454
|
.option('--json', 'Print machine-readable JSON output')
|
|
1934
2455
|
.action(async (options) => runStudioCommand(runStudioPlanCommand, options, 'plan'));
|
|
1935
2456
|
|
|
2457
|
+
studio
|
|
2458
|
+
.command('intake')
|
|
2459
|
+
.description('Analyze chat goal and auto-resolve spec binding/create decision')
|
|
2460
|
+
.requiredOption('--scene <scene-id>', 'Scene identifier')
|
|
2461
|
+
.requiredOption('--from-chat <session>', 'Chat session identifier or transcript reference')
|
|
2462
|
+
.option('--spec <spec-id>', 'Optional explicit spec id')
|
|
2463
|
+
.option('--goal <goal>', 'Goal text used for intent classification')
|
|
2464
|
+
.option('--apply', 'Create spec when decision is create_spec')
|
|
2465
|
+
.option('--manual-spec', 'Disable auto intake and keep explicit/manual binding only')
|
|
2466
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
2467
|
+
.action(async (options) => runStudioCommand(runStudioIntakeCommand, options, 'intake'));
|
|
2468
|
+
|
|
2469
|
+
studio
|
|
2470
|
+
.command('portfolio')
|
|
2471
|
+
.description('Build scene-organized spec governance portfolio')
|
|
2472
|
+
.option('--scene <scene-id>', 'Optional scene filter')
|
|
2473
|
+
.option('--no-apply', 'Do not write portfolio/index artifacts to .sce/spec-governance/')
|
|
2474
|
+
.option('--strict', 'Fail when governance alerts are detected')
|
|
2475
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
2476
|
+
.action(async (options) => runStudioCommand(runStudioPortfolioCommand, options, 'portfolio'));
|
|
2477
|
+
|
|
1936
2478
|
studio
|
|
1937
2479
|
.command('generate')
|
|
1938
2480
|
.description('Generate patch bundle metadata for a planned studio job (scene inherited from plan)')
|
|
@@ -2017,12 +2559,14 @@ module.exports = {
|
|
|
2017
2559
|
resolveNextAction,
|
|
2018
2560
|
buildProgress,
|
|
2019
2561
|
runStudioPlanCommand,
|
|
2562
|
+
runStudioIntakeCommand,
|
|
2020
2563
|
runStudioGenerateCommand,
|
|
2021
2564
|
runStudioApplyCommand,
|
|
2022
2565
|
runStudioVerifyCommand,
|
|
2023
2566
|
runStudioReleaseCommand,
|
|
2024
2567
|
runStudioRollbackCommand,
|
|
2025
2568
|
runStudioEventsCommand,
|
|
2569
|
+
runStudioPortfolioCommand,
|
|
2026
2570
|
runStudioResumeCommand,
|
|
2027
2571
|
registerStudioCommands
|
|
2028
2572
|
};
|