scene-capability-engine 3.6.32 → 3.6.36
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 +86 -1
- package/README.md +119 -122
- package/README.zh.md +123 -121
- package/bin/scene-capability-engine.js +11 -0
- package/docs/README.md +21 -32
- package/docs/auto-refactor-index.md +384 -0
- package/docs/command-reference.md +94 -2
- package/docs/magicball-adaptation-task-checklist-v1.md +385 -0
- package/docs/magicball-app-bundle-sqlite-and-command-draft.md +539 -0
- package/docs/magicball-capability-iteration-api.md +2 -0
- package/docs/magicball-capability-iteration-ui.md +2 -0
- package/docs/magicball-capability-library.md +2 -0
- package/docs/magicball-cli-invocation-examples.md +336 -0
- package/docs/magicball-frontend-state-and-command-mapping.md +244 -0
- package/docs/magicball-integration-doc-index.md +137 -0
- package/docs/magicball-integration-issue-tracker.md +218 -0
- package/docs/magicball-mode-home-and-ontology-empty-state-playbook.md +249 -0
- package/docs/magicball-sce-adaptation-guide.md +203 -0
- package/docs/magicball-three-mode-alignment-plan.md +551 -0
- package/docs/magicball-ui-surface-checklist.md +126 -0
- package/docs/magicball-write-auth-adaptation-guide.md +328 -0
- package/docs/refactor-completion-roadmap.md +116 -0
- package/docs/zh/README.md +27 -30
- package/docs/zh/refactor-completion-roadmap.md +116 -0
- package/lib/app/registry-config.js +73 -0
- package/lib/app/registry-sync-service.js +228 -0
- package/lib/auto/archive-schema-service.js +276 -0
- package/lib/auto/archive-summary.js +60 -0
- package/lib/auto/batch-goal-input-service.js +543 -0
- package/lib/auto/batch-output.js +201 -0
- package/lib/auto/batch-summary-storage-service.js +110 -0
- package/lib/auto/close-loop-batch-service.js +116 -0
- package/lib/auto/close-loop-controller-service.js +287 -0
- package/lib/auto/close-loop-program-service.js +283 -0
- package/lib/auto/close-loop-recovery-service.js +191 -0
- package/lib/auto/close-loop-session-storage-service.js +50 -0
- package/lib/auto/controller-lock-service.js +55 -0
- package/lib/auto/controller-output.js +32 -0
- package/lib/auto/controller-queue-service.js +127 -0
- package/lib/auto/controller-session-storage-service.js +105 -0
- package/lib/auto/governance-advisory-service.js +208 -0
- package/lib/auto/governance-close-loop-service.js +411 -0
- package/lib/auto/governance-maintenance-presenter.js +162 -0
- package/lib/auto/governance-maintenance-service.js +112 -0
- package/lib/auto/governance-session-presenter.js +70 -0
- package/lib/auto/governance-session-storage-service.js +198 -0
- package/lib/auto/governance-signals.js +139 -0
- package/lib/auto/governance-stats-presenter.js +337 -0
- package/lib/auto/governance-stats-service.js +115 -0
- package/lib/auto/governance-summary.js +703 -0
- package/lib/auto/handoff-capability-matrix-service.js +281 -0
- package/lib/auto/handoff-evidence-review-service.js +251 -0
- package/lib/auto/handoff-release-evidence-service.js +190 -0
- package/lib/auto/handoff-release-gate-history-loaders-service.js +502 -0
- package/lib/auto/handoff-release-gate-history-service.js +257 -0
- package/lib/auto/handoff-reporting-service.js +1407 -0
- package/lib/auto/handoff-run-service.js +486 -0
- package/lib/auto/handoff-snapshots-service.js +645 -0
- package/lib/auto/observability-service.js +132 -0
- package/lib/auto/output-writer.js +34 -0
- package/lib/auto/program-auto-remediation-service.js +130 -0
- package/lib/auto/program-diagnostics.js +138 -0
- package/lib/auto/program-governance-helpers.js +306 -0
- package/lib/auto/program-governance-loop-service.js +413 -0
- package/lib/auto/program-output.js +106 -0
- package/lib/auto/program-summary.js +183 -0
- package/lib/auto/recovery-memory-service.js +684 -0
- package/lib/auto/recovery-selection-service.js +52 -0
- package/lib/auto/retention-policy.js +98 -0
- package/lib/auto/session-persistence-service.js +106 -0
- package/lib/auto/session-presenter.js +105 -0
- package/lib/auto/session-prune-service.js +190 -0
- package/lib/auto/session-query-service.js +249 -0
- package/lib/auto/spec-protection.js +141 -0
- package/lib/commands/app.js +911 -0
- package/lib/commands/assurance.js +212 -0
- package/lib/commands/auto.js +1091 -11063
- package/lib/commands/mode.js +321 -0
- package/lib/commands/ontology.js +415 -0
- package/lib/commands/pm.js +422 -0
- package/lib/ontology/seed-profiles.js +160 -0
- package/lib/state/sce-state-store.js +3369 -1200
- package/package.json +1 -1
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
function buildAutoHandoffCapabilityMatrixPolicy(options = {}, dependencies = {}) {
|
|
2
|
+
const {
|
|
3
|
+
resolveAutoHandoffPolicyPreset,
|
|
4
|
+
normalizeHandoffMinCapabilityCoverage,
|
|
5
|
+
resolveAutoHandoffPolicyOptionNumber,
|
|
6
|
+
normalizeHandoffMinCapabilitySemantic,
|
|
7
|
+
resolveAutoHandoffPolicyOptionBoolean
|
|
8
|
+
} = dependencies;
|
|
9
|
+
const { profile, preset } = resolveAutoHandoffPolicyPreset(options.profile, '--profile');
|
|
10
|
+
return {
|
|
11
|
+
profile,
|
|
12
|
+
min_capability_coverage_percent: normalizeHandoffMinCapabilityCoverage(
|
|
13
|
+
resolveAutoHandoffPolicyOptionNumber(
|
|
14
|
+
options.minCapabilityCoverage,
|
|
15
|
+
preset.min_capability_coverage_percent
|
|
16
|
+
)
|
|
17
|
+
),
|
|
18
|
+
min_capability_semantic_percent: normalizeHandoffMinCapabilitySemantic(
|
|
19
|
+
resolveAutoHandoffPolicyOptionNumber(
|
|
20
|
+
options.minCapabilitySemantic,
|
|
21
|
+
preset.min_capability_semantic_percent
|
|
22
|
+
)
|
|
23
|
+
),
|
|
24
|
+
require_capability_coverage: resolveAutoHandoffPolicyOptionBoolean(
|
|
25
|
+
options.requireCapabilityCoverage,
|
|
26
|
+
preset.require_capability_coverage
|
|
27
|
+
),
|
|
28
|
+
require_capability_semantic: resolveAutoHandoffPolicyOptionBoolean(
|
|
29
|
+
options.requireCapabilitySemantic,
|
|
30
|
+
preset.require_capability_semantic
|
|
31
|
+
),
|
|
32
|
+
require_capability_lexicon: resolveAutoHandoffPolicyOptionBoolean(
|
|
33
|
+
options.requireCapabilityLexicon,
|
|
34
|
+
preset.require_capability_lexicon
|
|
35
|
+
),
|
|
36
|
+
require_moqui_baseline: resolveAutoHandoffPolicyOptionBoolean(
|
|
37
|
+
options.requireMoquiBaseline,
|
|
38
|
+
preset.require_moqui_baseline
|
|
39
|
+
)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildAutoHandoffCapabilityMatrixRecommendations(result = {}, dependencies = {}) {
|
|
44
|
+
const {
|
|
45
|
+
normalizeHandoffText,
|
|
46
|
+
quoteCliArg,
|
|
47
|
+
buildAutoHandoffMoquiCoverageRegressions,
|
|
48
|
+
buildMoquiRegressionRecoverySequenceLines,
|
|
49
|
+
clusterRemediationFile,
|
|
50
|
+
baselineJsonFile
|
|
51
|
+
} = dependencies;
|
|
52
|
+
|
|
53
|
+
const recommendations = [];
|
|
54
|
+
const push = value => {
|
|
55
|
+
const text = `${value || ''}`.trim();
|
|
56
|
+
if (!text || recommendations.includes(text)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
recommendations.push(text);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const manifestPath = normalizeHandoffText(result && result.manifest_path);
|
|
63
|
+
const manifestCli = manifestPath ? quoteCliArg(manifestPath) : '<path>';
|
|
64
|
+
const templateDiff = result && result.template_diff && typeof result.template_diff === 'object'
|
|
65
|
+
? result.template_diff
|
|
66
|
+
: {};
|
|
67
|
+
const capabilityCoverage = result && result.capability_coverage && typeof result.capability_coverage === 'object'
|
|
68
|
+
? result.capability_coverage
|
|
69
|
+
: {};
|
|
70
|
+
const coverageSummary = capabilityCoverage && capabilityCoverage.summary && typeof capabilityCoverage.summary === 'object'
|
|
71
|
+
? capabilityCoverage.summary
|
|
72
|
+
: {};
|
|
73
|
+
const coverageNormalization = capabilityCoverage && capabilityCoverage.normalization &&
|
|
74
|
+
typeof capabilityCoverage.normalization === 'object'
|
|
75
|
+
? capabilityCoverage.normalization
|
|
76
|
+
: {};
|
|
77
|
+
const expectedUnknownCount = Array.isArray(coverageNormalization.expected_unknown)
|
|
78
|
+
? coverageNormalization.expected_unknown.length
|
|
79
|
+
: 0;
|
|
80
|
+
const providedUnknownCount = Array.isArray(coverageNormalization.provided_unknown)
|
|
81
|
+
? coverageNormalization.provided_unknown.length
|
|
82
|
+
: 0;
|
|
83
|
+
const baseline = result && result.moqui_baseline && typeof result.moqui_baseline === 'object'
|
|
84
|
+
? result.moqui_baseline
|
|
85
|
+
: {};
|
|
86
|
+
const baselineCompare = baseline && baseline.compare && typeof baseline.compare === 'object'
|
|
87
|
+
? baseline.compare
|
|
88
|
+
: {};
|
|
89
|
+
const baselineRegressions = buildAutoHandoffMoquiCoverageRegressions(baselineCompare);
|
|
90
|
+
|
|
91
|
+
if (templateDiff.compatibility === 'needs-sync') {
|
|
92
|
+
push(`Sync template library and rerun: sce auto handoff template-diff --manifest ${manifestCli} --json`);
|
|
93
|
+
}
|
|
94
|
+
if (baseline.status === 'error' || (baseline.summary && baseline.summary.portfolio_passed === false)) {
|
|
95
|
+
push('Rebuild Moqui baseline: sce scene moqui-baseline --json');
|
|
96
|
+
}
|
|
97
|
+
if (baselineRegressions.length > 0) {
|
|
98
|
+
push(
|
|
99
|
+
`Recover Moqui matrix regressions: ` +
|
|
100
|
+
`${baselineRegressions.slice(0, 3).map(item => `${item.label}:${item.delta_rate_percent}%`).join(' | ')}`
|
|
101
|
+
);
|
|
102
|
+
for (const line of buildMoquiRegressionRecoverySequenceLines({
|
|
103
|
+
clusterGoalsArg: quoteCliArg(clusterRemediationFile),
|
|
104
|
+
baselineArg: quoteCliArg(baselineJsonFile),
|
|
105
|
+
wrapCommands: false,
|
|
106
|
+
withPeriod: false
|
|
107
|
+
})) {
|
|
108
|
+
push(line);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (capabilityCoverage.status === 'skipped') {
|
|
112
|
+
push('Declare `capabilities` in handoff manifest to enable capability matrix coverage gates.');
|
|
113
|
+
}
|
|
114
|
+
if (coverageSummary && coverageSummary.passed === false) {
|
|
115
|
+
push(
|
|
116
|
+
`Close capability gaps with strict gate: ` +
|
|
117
|
+
`sce auto handoff run --manifest ${manifestCli} --min-capability-coverage ${coverageSummary.min_required_percent} --json`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
if (coverageSummary && coverageSummary.semantic_passed === false) {
|
|
121
|
+
push(
|
|
122
|
+
`Backfill capability ontology semantics and rerun matrix: ` +
|
|
123
|
+
`sce scene package-ontology-backfill-batch --manifest ${manifestCli} --json`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (expectedUnknownCount > 0 || providedUnknownCount > 0) {
|
|
127
|
+
push(
|
|
128
|
+
`Normalize capability lexicon gaps with strict audit: ` +
|
|
129
|
+
`node scripts/moqui-lexicon-audit.js --manifest ${manifestCli} ` +
|
|
130
|
+
'--template-dir .sce/templates/scene-packages --fail-on-gap --json'
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
if (result.remediation_queue && result.remediation_queue.file) {
|
|
134
|
+
push(
|
|
135
|
+
`Replay remediation queue: sce auto close-loop-batch ${quoteCliArg(result.remediation_queue.file)} --format lines --json`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return recommendations;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function buildAutoHandoffCapabilityMatrix(projectPath, options = {}, dependencies = {}) {
|
|
143
|
+
const {
|
|
144
|
+
buildAutoHandoffPlan,
|
|
145
|
+
buildAutoHandoffCapabilityMatrixPolicy,
|
|
146
|
+
buildAutoHandoffTemplateDiff,
|
|
147
|
+
buildAutoHandoffMoquiBaselineSnapshot,
|
|
148
|
+
buildAutoHandoffCapabilityCoverageSnapshot,
|
|
149
|
+
evaluateAutoHandoffMoquiBaselineGateReasons,
|
|
150
|
+
evaluateAutoHandoffCapabilityCoverageGateReasons,
|
|
151
|
+
evaluateAutoHandoffCapabilitySemanticGateReasons,
|
|
152
|
+
evaluateAutoHandoffCapabilityLexiconGateReasons,
|
|
153
|
+
normalizeHandoffText,
|
|
154
|
+
maybeWriteAutoHandoffMoquiRemediationQueue,
|
|
155
|
+
buildAutoHandoffCapabilityMatrixRecommendations,
|
|
156
|
+
now = () => new Date().toISOString()
|
|
157
|
+
} = dependencies;
|
|
158
|
+
|
|
159
|
+
const plan = await buildAutoHandoffPlan(projectPath, {
|
|
160
|
+
manifest: options.manifest,
|
|
161
|
+
strict: options.strict,
|
|
162
|
+
strictWarnings: options.strictWarnings
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const policy = buildAutoHandoffCapabilityMatrixPolicy(options);
|
|
166
|
+
|
|
167
|
+
const [templateDiff, moquiBaseline, capabilityCoverage] = await Promise.all([
|
|
168
|
+
buildAutoHandoffTemplateDiff(projectPath, { handoff: plan.handoff }),
|
|
169
|
+
buildAutoHandoffMoquiBaselineSnapshot(projectPath),
|
|
170
|
+
buildAutoHandoffCapabilityCoverageSnapshot(projectPath, plan.handoff, policy)
|
|
171
|
+
]);
|
|
172
|
+
|
|
173
|
+
const templateSyncReasons = templateDiff.compatibility === 'ready'
|
|
174
|
+
? []
|
|
175
|
+
: [`template-sync:${templateDiff.compatibility}`];
|
|
176
|
+
const baselineGateReasons = evaluateAutoHandoffMoquiBaselineGateReasons(
|
|
177
|
+
{ require_moqui_baseline: true },
|
|
178
|
+
moquiBaseline
|
|
179
|
+
);
|
|
180
|
+
const capabilityGateReasons = evaluateAutoHandoffCapabilityCoverageGateReasons(
|
|
181
|
+
policy,
|
|
182
|
+
capabilityCoverage
|
|
183
|
+
);
|
|
184
|
+
const semanticGateReasons = evaluateAutoHandoffCapabilitySemanticGateReasons(
|
|
185
|
+
policy,
|
|
186
|
+
capabilityCoverage
|
|
187
|
+
);
|
|
188
|
+
const lexiconGateReasons = evaluateAutoHandoffCapabilityLexiconGateReasons(
|
|
189
|
+
policy,
|
|
190
|
+
capabilityCoverage
|
|
191
|
+
);
|
|
192
|
+
const reasons = [
|
|
193
|
+
...templateSyncReasons,
|
|
194
|
+
...baselineGateReasons.map(item => `moqui-baseline:${item}`),
|
|
195
|
+
...capabilityGateReasons.map(item => `capability-coverage:${item}`),
|
|
196
|
+
...semanticGateReasons.map(item => `capability-semantic:${item}`),
|
|
197
|
+
...lexiconGateReasons.map(item => `capability-lexicon:${item}`)
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
const result = {
|
|
201
|
+
mode: 'auto-handoff-capability-matrix',
|
|
202
|
+
generated_at: now(),
|
|
203
|
+
status: reasons.length === 0 ? 'ready' : 'needs-remediation',
|
|
204
|
+
manifest_path: plan.manifest_path,
|
|
205
|
+
source_project: plan.source_project || null,
|
|
206
|
+
handoff: {
|
|
207
|
+
spec_count: plan.handoff && Number.isFinite(Number(plan.handoff.spec_count))
|
|
208
|
+
? Number(plan.handoff.spec_count)
|
|
209
|
+
: 0,
|
|
210
|
+
template_count: plan.handoff && Number.isFinite(Number(plan.handoff.template_count))
|
|
211
|
+
? Number(plan.handoff.template_count)
|
|
212
|
+
: 0,
|
|
213
|
+
capability_count: Array.isArray(plan.handoff && plan.handoff.capabilities)
|
|
214
|
+
? plan.handoff.capabilities.length
|
|
215
|
+
: 0,
|
|
216
|
+
capability_source: normalizeHandoffText(plan.handoff && plan.handoff.capability_source) || 'manifest',
|
|
217
|
+
capability_inference: plan.handoff && plan.handoff.capability_inference &&
|
|
218
|
+
typeof plan.handoff.capability_inference === 'object'
|
|
219
|
+
? plan.handoff.capability_inference
|
|
220
|
+
: {
|
|
221
|
+
applied: false,
|
|
222
|
+
inferred_count: 0,
|
|
223
|
+
inferred_capabilities: [],
|
|
224
|
+
inferred_from_templates: [],
|
|
225
|
+
unresolved_template_count: 0,
|
|
226
|
+
unresolved_templates: []
|
|
227
|
+
},
|
|
228
|
+
capabilities: Array.isArray(plan.handoff && plan.handoff.capabilities)
|
|
229
|
+
? plan.handoff.capabilities
|
|
230
|
+
: []
|
|
231
|
+
},
|
|
232
|
+
policy,
|
|
233
|
+
template_diff: templateDiff,
|
|
234
|
+
moqui_baseline: moquiBaseline,
|
|
235
|
+
capability_coverage: capabilityCoverage,
|
|
236
|
+
gates: {
|
|
237
|
+
passed: reasons.length === 0,
|
|
238
|
+
reasons,
|
|
239
|
+
template_sync: {
|
|
240
|
+
passed: templateSyncReasons.length === 0,
|
|
241
|
+
reasons: templateSyncReasons
|
|
242
|
+
},
|
|
243
|
+
moqui_baseline: {
|
|
244
|
+
passed: baselineGateReasons.length === 0,
|
|
245
|
+
reasons: baselineGateReasons
|
|
246
|
+
},
|
|
247
|
+
capability_coverage: {
|
|
248
|
+
passed: capabilityGateReasons.length === 0,
|
|
249
|
+
reasons: capabilityGateReasons
|
|
250
|
+
},
|
|
251
|
+
capability_semantic: {
|
|
252
|
+
passed: semanticGateReasons.length === 0,
|
|
253
|
+
reasons: semanticGateReasons
|
|
254
|
+
},
|
|
255
|
+
capability_lexicon: {
|
|
256
|
+
passed: lexiconGateReasons.length === 0,
|
|
257
|
+
reasons: lexiconGateReasons
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
remediation_queue: null,
|
|
261
|
+
recommendations: []
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
result.remediation_queue = await maybeWriteAutoHandoffMoquiRemediationQueue(
|
|
265
|
+
projectPath,
|
|
266
|
+
{
|
|
267
|
+
moqui_baseline: moquiBaseline,
|
|
268
|
+
moqui_capability_coverage: capabilityCoverage
|
|
269
|
+
},
|
|
270
|
+
options.remediationQueueOut
|
|
271
|
+
);
|
|
272
|
+
result.recommendations = buildAutoHandoffCapabilityMatrixRecommendations(result);
|
|
273
|
+
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
module.exports = {
|
|
278
|
+
buildAutoHandoffCapabilityMatrixPolicy,
|
|
279
|
+
buildAutoHandoffCapabilityMatrixRecommendations,
|
|
280
|
+
buildAutoHandoffCapabilityMatrix
|
|
281
|
+
};
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
function buildAutoHandoffEvidenceSnapshot(entry = {}, dependencies = {}) {
|
|
2
|
+
const {
|
|
3
|
+
normalizeHandoffText,
|
|
4
|
+
buildAutoHandoffMoquiCoverageRegressions,
|
|
5
|
+
normalizeRiskRank
|
|
6
|
+
} = dependencies;
|
|
7
|
+
const toNumber = value => {
|
|
8
|
+
const parsed = Number(value);
|
|
9
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
10
|
+
};
|
|
11
|
+
const gate = entry && typeof entry.gate === 'object' ? entry.gate : {};
|
|
12
|
+
const gateActual = gate && typeof gate.actual === 'object' ? gate.actual : {};
|
|
13
|
+
const ontology = entry && typeof entry.ontology_validation === 'object'
|
|
14
|
+
? entry.ontology_validation
|
|
15
|
+
: {};
|
|
16
|
+
const ontologyMetrics = ontology && typeof ontology.metrics === 'object'
|
|
17
|
+
? ontology.metrics
|
|
18
|
+
: {};
|
|
19
|
+
const moquiBaseline = entry && typeof entry.moqui_baseline === 'object'
|
|
20
|
+
? entry.moqui_baseline
|
|
21
|
+
: {};
|
|
22
|
+
const moquiCompare = moquiBaseline && typeof moquiBaseline.compare === 'object'
|
|
23
|
+
? moquiBaseline.compare
|
|
24
|
+
: {};
|
|
25
|
+
const moquiMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
|
|
26
|
+
const scenePackageBatch = entry && typeof entry.scene_package_batch === 'object'
|
|
27
|
+
? entry.scene_package_batch
|
|
28
|
+
: {};
|
|
29
|
+
const scenePackageBatchSummary = scenePackageBatch && typeof scenePackageBatch.summary === 'object'
|
|
30
|
+
? scenePackageBatch.summary
|
|
31
|
+
: {};
|
|
32
|
+
const sceneBatchStatus = normalizeHandoffText(scenePackageBatch.status);
|
|
33
|
+
const sceneBatchPassed = sceneBatchStatus
|
|
34
|
+
? (sceneBatchStatus === 'skipped' ? null : sceneBatchStatus === 'passed')
|
|
35
|
+
: null;
|
|
36
|
+
const riskLevel = normalizeHandoffText(
|
|
37
|
+
gateActual.risk_level
|
|
38
|
+
|| (entry && entry.regression ? entry.regression.risk_level : null)
|
|
39
|
+
|| 'high'
|
|
40
|
+
) || 'high';
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
session_id: normalizeHandoffText(entry.session_id),
|
|
44
|
+
status: normalizeHandoffText(entry.status),
|
|
45
|
+
merged_at: normalizeHandoffText(entry.merged_at),
|
|
46
|
+
manifest_path: normalizeHandoffText(entry.manifest_path),
|
|
47
|
+
gate_passed: gate.passed === true,
|
|
48
|
+
spec_success_rate_percent: toNumber(gateActual.spec_success_rate_percent),
|
|
49
|
+
risk_level: `${riskLevel}`.trim().toLowerCase(),
|
|
50
|
+
risk_level_rank: normalizeRiskRank(riskLevel),
|
|
51
|
+
failed_goals: toNumber(entry && entry.batch_summary ? entry.batch_summary.failed_goals : null),
|
|
52
|
+
elapsed_ms: null,
|
|
53
|
+
ontology_quality_score: toNumber(
|
|
54
|
+
gateActual.ontology_quality_score !== undefined
|
|
55
|
+
? gateActual.ontology_quality_score
|
|
56
|
+
: ontology.quality_score
|
|
57
|
+
),
|
|
58
|
+
ontology_unmapped_rules: toNumber(
|
|
59
|
+
gateActual.ontology_business_rule_unmapped !== undefined
|
|
60
|
+
? gateActual.ontology_business_rule_unmapped
|
|
61
|
+
: ontologyMetrics.business_rule_unmapped
|
|
62
|
+
),
|
|
63
|
+
ontology_undecided_decisions: toNumber(
|
|
64
|
+
gateActual.ontology_decision_undecided !== undefined
|
|
65
|
+
? gateActual.ontology_decision_undecided
|
|
66
|
+
: ontologyMetrics.decision_undecided
|
|
67
|
+
),
|
|
68
|
+
ontology_business_rule_pass_rate_percent: toNumber(ontologyMetrics.business_rule_pass_rate_percent),
|
|
69
|
+
ontology_decision_resolved_rate_percent: toNumber(ontologyMetrics.decision_resolved_rate_percent),
|
|
70
|
+
scene_package_batch_status: sceneBatchStatus,
|
|
71
|
+
scene_package_batch_passed: typeof sceneBatchPassed === 'boolean' ? sceneBatchPassed : null,
|
|
72
|
+
scene_package_batch_failure_count: toNumber(
|
|
73
|
+
scenePackageBatchSummary.batch_gate_failure_count !== undefined
|
|
74
|
+
? scenePackageBatchSummary.batch_gate_failure_count
|
|
75
|
+
: scenePackageBatchSummary.failed
|
|
76
|
+
),
|
|
77
|
+
capability_coverage_percent: toNumber(
|
|
78
|
+
entry &&
|
|
79
|
+
entry.capability_coverage &&
|
|
80
|
+
entry.capability_coverage.summary
|
|
81
|
+
? entry.capability_coverage.summary.coverage_percent
|
|
82
|
+
: null
|
|
83
|
+
),
|
|
84
|
+
capability_coverage_passed: Boolean(
|
|
85
|
+
entry &&
|
|
86
|
+
entry.capability_coverage &&
|
|
87
|
+
entry.capability_coverage.summary &&
|
|
88
|
+
entry.capability_coverage.summary.passed === true
|
|
89
|
+
),
|
|
90
|
+
moqui_matrix_regression_count: moquiMatrixRegressions.length,
|
|
91
|
+
generated_at: normalizeHandoffText(entry.merged_at)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function buildAutoHandoffEvidenceStatusCounts(entries = []) {
|
|
96
|
+
const counts = {
|
|
97
|
+
completed: 0,
|
|
98
|
+
failed: 0,
|
|
99
|
+
dry_run: 0,
|
|
100
|
+
running: 0,
|
|
101
|
+
other: 0
|
|
102
|
+
};
|
|
103
|
+
entries.forEach(entry => {
|
|
104
|
+
const status = `${entry && entry.status ? entry.status : ''}`.trim().toLowerCase();
|
|
105
|
+
if (status === 'completed') {
|
|
106
|
+
counts.completed += 1;
|
|
107
|
+
} else if (status === 'failed') {
|
|
108
|
+
counts.failed += 1;
|
|
109
|
+
} else if (status === 'dry-run' || status === 'dry_run') {
|
|
110
|
+
counts.dry_run += 1;
|
|
111
|
+
} else if (status === 'running') {
|
|
112
|
+
counts.running += 1;
|
|
113
|
+
} else {
|
|
114
|
+
counts.other += 1;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return counts;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function resolveAutoHandoffReleaseDraftContext(projectPath, options = {}, dependencies = {}) {
|
|
121
|
+
const { fs, pathModule } = dependencies;
|
|
122
|
+
let packageVersion = null;
|
|
123
|
+
try {
|
|
124
|
+
const packagePayload = await fs.readJson(pathModule.join(projectPath, 'package.json'));
|
|
125
|
+
if (packagePayload && typeof packagePayload.version === 'string' && packagePayload.version.trim()) {
|
|
126
|
+
packageVersion = packagePayload.version.trim();
|
|
127
|
+
}
|
|
128
|
+
} catch (_error) {
|
|
129
|
+
packageVersion = null;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
version: dependencies.normalizeHandoffReleaseVersion(options.releaseVersion, packageVersion || '0.0.0'),
|
|
133
|
+
releaseDate: dependencies.normalizeHandoffReleaseDate(options.releaseDate)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function buildAutoHandoffEvidenceReviewReport(projectPath, options = {}, dependencies = {}) {
|
|
138
|
+
const {
|
|
139
|
+
loadAutoHandoffReleaseEvidence,
|
|
140
|
+
normalizeHandoffSessionQuery,
|
|
141
|
+
normalizeHandoffEvidenceWindow,
|
|
142
|
+
normalizeHandoffText,
|
|
143
|
+
buildAutoHandoffEvidenceSnapshot,
|
|
144
|
+
buildAutoHandoffRegressionComparison,
|
|
145
|
+
buildAutoHandoffRegressionWindowTrend,
|
|
146
|
+
buildAutoHandoffRegressionAggregates,
|
|
147
|
+
buildAutoHandoffRegressionRiskLayers,
|
|
148
|
+
buildAutoHandoffEvidenceStatusCounts,
|
|
149
|
+
buildAutoGovernanceStats,
|
|
150
|
+
buildAutoHandoffRegressionRecommendations,
|
|
151
|
+
now = () => new Date().toISOString()
|
|
152
|
+
} = dependencies;
|
|
153
|
+
|
|
154
|
+
const releaseEvidence = await loadAutoHandoffReleaseEvidence(projectPath, options.file);
|
|
155
|
+
if (releaseEvidence.sessions.length === 0) {
|
|
156
|
+
throw new Error(`no release evidence sessions found: ${releaseEvidence.file}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const query = normalizeHandoffSessionQuery(options.sessionId);
|
|
160
|
+
const windowSize = normalizeHandoffEvidenceWindow(options.window);
|
|
161
|
+
let currentIndex = 0;
|
|
162
|
+
if (query !== 'latest') {
|
|
163
|
+
currentIndex = releaseEvidence.sessions.findIndex(item => normalizeHandoffText(item.session_id) === query);
|
|
164
|
+
if (currentIndex < 0) {
|
|
165
|
+
throw new Error(`release evidence session not found: ${query}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const selectedEntries = releaseEvidence.sessions.slice(currentIndex, currentIndex + windowSize);
|
|
170
|
+
const series = selectedEntries.map(item => buildAutoHandoffEvidenceSnapshot(item));
|
|
171
|
+
const currentSnapshot = series[0];
|
|
172
|
+
const previousSnapshot = series[1] || null;
|
|
173
|
+
const comparison = previousSnapshot
|
|
174
|
+
? buildAutoHandoffRegressionComparison(currentSnapshot, previousSnapshot)
|
|
175
|
+
: {
|
|
176
|
+
trend: 'baseline',
|
|
177
|
+
delta: {
|
|
178
|
+
spec_success_rate_percent: null,
|
|
179
|
+
risk_level_rank: null,
|
|
180
|
+
failed_goals: null,
|
|
181
|
+
elapsed_ms: null,
|
|
182
|
+
ontology_quality_score: null,
|
|
183
|
+
ontology_unmapped_rules: null,
|
|
184
|
+
ontology_undecided_decisions: null,
|
|
185
|
+
ontology_business_rule_pass_rate_percent: null,
|
|
186
|
+
ontology_decision_resolved_rate_percent: null,
|
|
187
|
+
scene_package_batch_failure_count: null
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const windowTrend = buildAutoHandoffRegressionWindowTrend(series);
|
|
191
|
+
const aggregates = buildAutoHandoffRegressionAggregates(series);
|
|
192
|
+
const riskLayers = buildAutoHandoffRegressionRiskLayers(series);
|
|
193
|
+
const statusCounts = buildAutoHandoffEvidenceStatusCounts(selectedEntries);
|
|
194
|
+
const gatePassCount = selectedEntries.filter(item => item && item.gate && item.gate.passed === true).length;
|
|
195
|
+
const gatePassRate = selectedEntries.length > 0
|
|
196
|
+
? Number(((gatePassCount / selectedEntries.length) * 100).toFixed(2))
|
|
197
|
+
: null;
|
|
198
|
+
let governanceSnapshot = null;
|
|
199
|
+
try {
|
|
200
|
+
const governanceStats = await buildAutoGovernanceStats(projectPath, {});
|
|
201
|
+
governanceSnapshot = {
|
|
202
|
+
mode: governanceStats && governanceStats.mode ? governanceStats.mode : 'auto-governance-stats',
|
|
203
|
+
generated_at: governanceStats && governanceStats.generated_at ? governanceStats.generated_at : now(),
|
|
204
|
+
criteria: governanceStats && governanceStats.criteria ? governanceStats.criteria : null,
|
|
205
|
+
totals: governanceStats && governanceStats.totals ? governanceStats.totals : null,
|
|
206
|
+
health: governanceStats && governanceStats.health ? governanceStats.health : null
|
|
207
|
+
};
|
|
208
|
+
} catch (error) {
|
|
209
|
+
governanceSnapshot = {
|
|
210
|
+
mode: 'auto-governance-stats',
|
|
211
|
+
generated_at: now(),
|
|
212
|
+
error: error.message
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const payload = {
|
|
217
|
+
mode: 'auto-handoff-evidence-review',
|
|
218
|
+
generated_at: now(),
|
|
219
|
+
evidence_file: releaseEvidence.file,
|
|
220
|
+
release_evidence_updated_at: normalizeHandoffText(releaseEvidence.payload.updated_at),
|
|
221
|
+
session_query: query,
|
|
222
|
+
current: currentSnapshot,
|
|
223
|
+
current_overview: selectedEntries[0] || null,
|
|
224
|
+
previous: previousSnapshot,
|
|
225
|
+
trend: comparison.trend,
|
|
226
|
+
delta: comparison.delta,
|
|
227
|
+
window: {
|
|
228
|
+
requested: windowSize,
|
|
229
|
+
actual: series.length
|
|
230
|
+
},
|
|
231
|
+
series,
|
|
232
|
+
window_trend: windowTrend,
|
|
233
|
+
aggregates: {
|
|
234
|
+
...aggregates,
|
|
235
|
+
status_counts: statusCounts,
|
|
236
|
+
gate_pass_rate_percent: gatePassRate
|
|
237
|
+
},
|
|
238
|
+
risk_layers: riskLayers,
|
|
239
|
+
governance_snapshot: governanceSnapshot,
|
|
240
|
+
recommendations: []
|
|
241
|
+
};
|
|
242
|
+
payload.recommendations = buildAutoHandoffRegressionRecommendations(payload);
|
|
243
|
+
return payload;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
module.exports = {
|
|
247
|
+
buildAutoHandoffEvidenceSnapshot,
|
|
248
|
+
buildAutoHandoffEvidenceStatusCounts,
|
|
249
|
+
resolveAutoHandoffReleaseDraftContext,
|
|
250
|
+
buildAutoHandoffEvidenceReviewReport
|
|
251
|
+
};
|