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,190 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
async function loadAutoHandoffReleaseEvidence(projectPath, fileCandidate = null, dependencies = {}) {
|
|
4
|
+
const { resolveAutoHandoffReleaseEvidenceFile, fs } = dependencies;
|
|
5
|
+
const filePath = resolveAutoHandoffReleaseEvidenceFile(projectPath, fileCandidate);
|
|
6
|
+
if (!(await fs.pathExists(filePath))) {
|
|
7
|
+
throw new Error(`release evidence file not found: ${filePath}`);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let payload = null;
|
|
11
|
+
try {
|
|
12
|
+
payload = await fs.readJson(filePath);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
throw new Error(`invalid release evidence JSON: ${filePath} (${error.message})`);
|
|
15
|
+
}
|
|
16
|
+
if (!payload || typeof payload !== 'object') {
|
|
17
|
+
throw new Error(`invalid release evidence payload: ${filePath}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const sessions = Array.isArray(payload.sessions)
|
|
21
|
+
? payload.sessions.filter(item => item && typeof item === 'object')
|
|
22
|
+
: [];
|
|
23
|
+
sessions.sort((left, right) => {
|
|
24
|
+
const leftTs = Date.parse(
|
|
25
|
+
left && (left.merged_at || left.generated_at || left.updated_at)
|
|
26
|
+
? (left.merged_at || left.generated_at || left.updated_at)
|
|
27
|
+
: 0
|
|
28
|
+
);
|
|
29
|
+
const rightTs = Date.parse(
|
|
30
|
+
right && (right.merged_at || right.generated_at || right.updated_at)
|
|
31
|
+
? (right.merged_at || right.generated_at || right.updated_at)
|
|
32
|
+
: 0
|
|
33
|
+
);
|
|
34
|
+
return (Number.isFinite(rightTs) ? rightTs : 0) - (Number.isFinite(leftTs) ? leftTs : 0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
file: filePath,
|
|
39
|
+
payload,
|
|
40
|
+
sessions
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function mergeAutoHandoffRunIntoReleaseEvidence(projectPath, result, reportFile = null, dependencies = {}) {
|
|
45
|
+
const {
|
|
46
|
+
resolveAutoHandoffReleaseEvidenceFile,
|
|
47
|
+
normalizeHandoffReleaseEvidenceWindow,
|
|
48
|
+
loadAutoHandoffReleaseEvidence,
|
|
49
|
+
fs,
|
|
50
|
+
buildAutoHandoffRegressionReport,
|
|
51
|
+
normalizeHandoffText,
|
|
52
|
+
buildAutoHandoffReleaseEvidenceEntry,
|
|
53
|
+
now = () => new Date().toISOString()
|
|
54
|
+
} = dependencies;
|
|
55
|
+
|
|
56
|
+
const evidenceFile = resolveAutoHandoffReleaseEvidenceFile(projectPath);
|
|
57
|
+
if (result && result.dry_run) {
|
|
58
|
+
return {
|
|
59
|
+
mode: 'auto-handoff-release-evidence',
|
|
60
|
+
merged: false,
|
|
61
|
+
skipped: true,
|
|
62
|
+
reason: 'dry-run',
|
|
63
|
+
file: evidenceFile
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let existing = null;
|
|
68
|
+
try {
|
|
69
|
+
existing = await loadAutoHandoffReleaseEvidence(projectPath, evidenceFile);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (!(error && typeof error.message === 'string' && error.message.includes('release evidence file not found'))) {
|
|
72
|
+
throw new Error(`failed to read release evidence JSON: ${evidenceFile} (${error.message})`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const existingSessions = existing && Array.isArray(existing.sessions)
|
|
77
|
+
? existing.sessions.filter(item => item && typeof item === 'object')
|
|
78
|
+
: [];
|
|
79
|
+
const nowIso = now();
|
|
80
|
+
let trendWindow = null;
|
|
81
|
+
const trendWindowSize = Number(
|
|
82
|
+
result &&
|
|
83
|
+
result.policy &&
|
|
84
|
+
result.policy.release_evidence_window !== undefined &&
|
|
85
|
+
result.policy.release_evidence_window !== null
|
|
86
|
+
? result.policy.release_evidence_window
|
|
87
|
+
: 5
|
|
88
|
+
);
|
|
89
|
+
if (Number.isInteger(trendWindowSize) && trendWindowSize >= 2 && trendWindowSize <= 50) {
|
|
90
|
+
try {
|
|
91
|
+
const regressionSnapshot = await buildAutoHandoffRegressionReport(projectPath, {
|
|
92
|
+
sessionId: result && result.session_id ? result.session_id : 'latest',
|
|
93
|
+
window: trendWindowSize
|
|
94
|
+
});
|
|
95
|
+
trendWindow = {
|
|
96
|
+
generated_at: nowIso,
|
|
97
|
+
window: regressionSnapshot.window || {
|
|
98
|
+
requested: trendWindowSize,
|
|
99
|
+
actual: null
|
|
100
|
+
},
|
|
101
|
+
trend: normalizeHandoffText(regressionSnapshot.trend),
|
|
102
|
+
window_trend: regressionSnapshot.window_trend || null,
|
|
103
|
+
aggregates: regressionSnapshot.aggregates || null,
|
|
104
|
+
risk_layers: regressionSnapshot.risk_layers || null
|
|
105
|
+
};
|
|
106
|
+
} catch (error) {
|
|
107
|
+
trendWindow = {
|
|
108
|
+
generated_at: nowIso,
|
|
109
|
+
window: {
|
|
110
|
+
requested: trendWindowSize,
|
|
111
|
+
actual: null
|
|
112
|
+
},
|
|
113
|
+
error: error && error.message ? error.message : `${error}`
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const nextEntry = buildAutoHandoffReleaseEvidenceEntry(projectPath, result, reportFile, trendWindow);
|
|
119
|
+
const sessionId = normalizeHandoffText(nextEntry.session_id);
|
|
120
|
+
let updatedExisting = false;
|
|
121
|
+
const mergedSessions = existingSessions.slice();
|
|
122
|
+
|
|
123
|
+
if (sessionId) {
|
|
124
|
+
const existingIndex = mergedSessions.findIndex(item => normalizeHandoffText(item.session_id) === sessionId);
|
|
125
|
+
if (existingIndex >= 0) {
|
|
126
|
+
mergedSessions[existingIndex] = {
|
|
127
|
+
...mergedSessions[existingIndex],
|
|
128
|
+
...nextEntry
|
|
129
|
+
};
|
|
130
|
+
updatedExisting = true;
|
|
131
|
+
} else {
|
|
132
|
+
mergedSessions.push(nextEntry);
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
mergedSessions.push(nextEntry);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
mergedSessions.sort((left, right) => {
|
|
139
|
+
const leftTs = Date.parse(left && (left.merged_at || left.generated_at || left.updated_at) ? (left.merged_at || left.generated_at || left.updated_at) : 0);
|
|
140
|
+
const rightTs = Date.parse(right && (right.merged_at || right.generated_at || right.updated_at) ? (right.merged_at || right.generated_at || right.updated_at) : 0);
|
|
141
|
+
return (Number.isFinite(rightTs) ? rightTs : 0) - (Number.isFinite(leftTs) ? leftTs : 0);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const generatedAt = existing && existing.payload && typeof existing.payload.generated_at === 'string' && existing.payload.generated_at.trim()
|
|
145
|
+
? existing.payload.generated_at
|
|
146
|
+
: nowIso;
|
|
147
|
+
const payload = {
|
|
148
|
+
mode: 'auto-handoff-release-evidence',
|
|
149
|
+
generated_at: generatedAt,
|
|
150
|
+
updated_at: nowIso,
|
|
151
|
+
latest_session_id: sessionId || (
|
|
152
|
+
mergedSessions.length > 0 && normalizeHandoffText(mergedSessions[0].session_id)
|
|
153
|
+
? normalizeHandoffText(mergedSessions[0].session_id)
|
|
154
|
+
: null
|
|
155
|
+
),
|
|
156
|
+
total_runs: mergedSessions.length,
|
|
157
|
+
latest_trend_window: mergedSessions.length > 0 && mergedSessions[0] && mergedSessions[0].trend_window
|
|
158
|
+
? mergedSessions[0].trend_window
|
|
159
|
+
: null,
|
|
160
|
+
sessions: mergedSessions
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
await fs.ensureDir(path.dirname(evidenceFile));
|
|
164
|
+
await fs.writeJson(evidenceFile, payload, { spaces: 2 });
|
|
165
|
+
return {
|
|
166
|
+
mode: 'auto-handoff-release-evidence',
|
|
167
|
+
merged: true,
|
|
168
|
+
updated_existing: updatedExisting,
|
|
169
|
+
file: evidenceFile,
|
|
170
|
+
latest_session_id: payload.latest_session_id,
|
|
171
|
+
total_runs: payload.total_runs,
|
|
172
|
+
trend_window: nextEntry.trend_window
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function writeAutoHandoffRunReport(projectPath, result, outCandidate = null, dependencies = {}) {
|
|
177
|
+
const { maybeWriteOutput, reportDir } = dependencies;
|
|
178
|
+
if (typeof outCandidate === 'string' && outCandidate.trim().length > 0) {
|
|
179
|
+
await maybeWriteOutput(result, outCandidate.trim(), projectPath);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const defaultFile = path.join(reportDir, `${result.session_id}.json`);
|
|
183
|
+
await maybeWriteOutput(result, defaultFile, projectPath);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = {
|
|
187
|
+
loadAutoHandoffReleaseEvidence,
|
|
188
|
+
mergeAutoHandoffRunIntoReleaseEvidence,
|
|
189
|
+
writeAutoHandoffRunReport
|
|
190
|
+
};
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
function buildAutoHandoffReleaseGateHistoryEntry(entry = {}, options = {}, dependencies = {}) {
|
|
4
|
+
const { parseAutoHandoffGateSignalsMap, normalizeHandoffText, parseAutoHandoffReleaseGateTag, parseAutoHandoffGateBoolean, normalizeAutoHandoffGateRiskLevel, parseAutoHandoffGateNumber, toPortablePath } = dependencies;
|
|
5
|
+
const projectPath = options.projectPath || process.cwd();
|
|
6
|
+
const sourceFile = typeof options.file === 'string' && options.file.trim()
|
|
7
|
+
? options.file.trim()
|
|
8
|
+
: null;
|
|
9
|
+
const signalMap = parseAutoHandoffGateSignalsMap(entry.signals);
|
|
10
|
+
const derivedTag = normalizeHandoffText(options.tag)
|
|
11
|
+
|| (sourceFile ? parseAutoHandoffReleaseGateTag(path.basename(sourceFile)) : null)
|
|
12
|
+
|| normalizeHandoffText(entry.tag);
|
|
13
|
+
const gatePassed = parseAutoHandoffGateBoolean(
|
|
14
|
+
entry.gate_passed !== undefined ? entry.gate_passed : signalMap.gate_passed,
|
|
15
|
+
null
|
|
16
|
+
);
|
|
17
|
+
const riskLevel = normalizeAutoHandoffGateRiskLevel(
|
|
18
|
+
normalizeHandoffText(entry.risk_level) || signalMap.risk_level
|
|
19
|
+
);
|
|
20
|
+
const specSuccessRate = parseAutoHandoffGateNumber(
|
|
21
|
+
entry.spec_success_rate_percent !== undefined
|
|
22
|
+
? entry.spec_success_rate_percent
|
|
23
|
+
: signalMap.spec_success_rate
|
|
24
|
+
);
|
|
25
|
+
const sceneBatchStatus = normalizeHandoffText(
|
|
26
|
+
entry.scene_package_batch_status !== undefined
|
|
27
|
+
? entry.scene_package_batch_status
|
|
28
|
+
: signalMap.scene_package_batch_status
|
|
29
|
+
);
|
|
30
|
+
let sceneBatchPassed = parseAutoHandoffGateBoolean(
|
|
31
|
+
entry.scene_package_batch_passed !== undefined
|
|
32
|
+
? entry.scene_package_batch_passed
|
|
33
|
+
: signalMap.scene_package_batch_passed,
|
|
34
|
+
null
|
|
35
|
+
);
|
|
36
|
+
if (sceneBatchPassed === null && sceneBatchStatus && sceneBatchStatus !== 'skipped') {
|
|
37
|
+
sceneBatchPassed = sceneBatchStatus === 'passed';
|
|
38
|
+
}
|
|
39
|
+
const sceneBatchFailureCount = parseAutoHandoffGateNumber(
|
|
40
|
+
entry.scene_package_batch_failure_count !== undefined
|
|
41
|
+
? entry.scene_package_batch_failure_count
|
|
42
|
+
: signalMap.scene_package_batch_failure_count
|
|
43
|
+
);
|
|
44
|
+
const capabilityExpectedUnknownCount = parseAutoHandoffGateNumber(
|
|
45
|
+
entry.capability_expected_unknown_count !== undefined
|
|
46
|
+
? entry.capability_expected_unknown_count
|
|
47
|
+
: (
|
|
48
|
+
signalMap.capability_expected_unknown_count !== undefined
|
|
49
|
+
? signalMap.capability_expected_unknown_count
|
|
50
|
+
: signalMap.capability_lexicon_expected_unknown_count
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
const capabilityProvidedUnknownCount = parseAutoHandoffGateNumber(
|
|
54
|
+
entry.capability_provided_unknown_count !== undefined
|
|
55
|
+
? entry.capability_provided_unknown_count
|
|
56
|
+
: (
|
|
57
|
+
signalMap.capability_provided_unknown_count !== undefined
|
|
58
|
+
? signalMap.capability_provided_unknown_count
|
|
59
|
+
: signalMap.capability_lexicon_provided_unknown_count
|
|
60
|
+
)
|
|
61
|
+
);
|
|
62
|
+
const releaseGatePreflightAvailable = parseAutoHandoffGateBoolean(
|
|
63
|
+
entry.release_gate_preflight_available !== undefined
|
|
64
|
+
? entry.release_gate_preflight_available
|
|
65
|
+
: signalMap.release_gate_preflight_available,
|
|
66
|
+
null
|
|
67
|
+
);
|
|
68
|
+
const releaseGatePreflightBlocked = parseAutoHandoffGateBoolean(
|
|
69
|
+
entry.release_gate_preflight_blocked !== undefined
|
|
70
|
+
? entry.release_gate_preflight_blocked
|
|
71
|
+
: signalMap.release_gate_preflight_blocked,
|
|
72
|
+
null
|
|
73
|
+
);
|
|
74
|
+
const requireReleaseGatePreflight = parseAutoHandoffGateBoolean(
|
|
75
|
+
entry.require_release_gate_preflight !== undefined
|
|
76
|
+
? entry.require_release_gate_preflight
|
|
77
|
+
: (
|
|
78
|
+
signalMap.require_release_gate_preflight !== undefined
|
|
79
|
+
? signalMap.require_release_gate_preflight
|
|
80
|
+
: signalMap.release_gate_preflight_hard_gate
|
|
81
|
+
),
|
|
82
|
+
null
|
|
83
|
+
);
|
|
84
|
+
const drift = entry && typeof entry.drift === 'object' && !Array.isArray(entry.drift)
|
|
85
|
+
? entry.drift
|
|
86
|
+
: {};
|
|
87
|
+
const driftAlerts = Array.isArray(drift.alerts)
|
|
88
|
+
? drift.alerts
|
|
89
|
+
.map(item => `${item || ''}`.trim())
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
: [];
|
|
92
|
+
const hasDriftAlertSource = (
|
|
93
|
+
entry.drift_alert_count !== undefined
|
|
94
|
+
|| drift.alert_count !== undefined
|
|
95
|
+
|| Array.isArray(drift.alerts)
|
|
96
|
+
);
|
|
97
|
+
const driftAlertCount = hasDriftAlertSource
|
|
98
|
+
? parseAutoHandoffGateNumber(
|
|
99
|
+
entry.drift_alert_count !== undefined
|
|
100
|
+
? entry.drift_alert_count
|
|
101
|
+
: (drift.alert_count !== undefined ? drift.alert_count : driftAlerts.length)
|
|
102
|
+
)
|
|
103
|
+
: null;
|
|
104
|
+
const driftBlocked = parseAutoHandoffGateBoolean(
|
|
105
|
+
entry.drift_blocked !== undefined
|
|
106
|
+
? entry.drift_blocked
|
|
107
|
+
: drift.blocked,
|
|
108
|
+
null
|
|
109
|
+
);
|
|
110
|
+
const driftEnforce = parseAutoHandoffGateBoolean(
|
|
111
|
+
entry.drift_enforce !== undefined
|
|
112
|
+
? entry.drift_enforce
|
|
113
|
+
: drift.enforce,
|
|
114
|
+
null
|
|
115
|
+
);
|
|
116
|
+
const driftEvaluatedAt = normalizeHandoffText(
|
|
117
|
+
entry.drift_evaluated_at !== undefined
|
|
118
|
+
? entry.drift_evaluated_at
|
|
119
|
+
: drift.evaluated_at
|
|
120
|
+
);
|
|
121
|
+
const weeklyOps = entry && typeof entry.weekly_ops === 'object' && !Array.isArray(entry.weekly_ops)
|
|
122
|
+
? entry.weekly_ops
|
|
123
|
+
: {};
|
|
124
|
+
const weeklyOpsSignals = weeklyOps && typeof weeklyOps.signals === 'object' && !Array.isArray(weeklyOps.signals)
|
|
125
|
+
? weeklyOps.signals
|
|
126
|
+
: {};
|
|
127
|
+
const weeklyOpsViolations = Array.isArray(weeklyOps.violations)
|
|
128
|
+
? weeklyOps.violations.map(item => `${item}`)
|
|
129
|
+
: [];
|
|
130
|
+
const weeklyOpsWarnings = Array.isArray(weeklyOps.warnings)
|
|
131
|
+
? weeklyOps.warnings.map(item => `${item}`)
|
|
132
|
+
: [];
|
|
133
|
+
const weeklyOpsConfigWarnings = Array.isArray(weeklyOps.config_warnings)
|
|
134
|
+
? weeklyOps.config_warnings.map(item => `${item}`)
|
|
135
|
+
: [];
|
|
136
|
+
const weeklyOpsAvailable = parseAutoHandoffGateBoolean(entry.weekly_ops_available, null) === true
|
|
137
|
+
|| Object.keys(weeklyOps).length > 0;
|
|
138
|
+
const weeklyOpsBlocked = parseAutoHandoffGateBoolean(
|
|
139
|
+
entry.weekly_ops_blocked !== undefined
|
|
140
|
+
? entry.weekly_ops_blocked
|
|
141
|
+
: weeklyOps.blocked,
|
|
142
|
+
null
|
|
143
|
+
);
|
|
144
|
+
const weeklyOpsRiskRaw = normalizeHandoffText(
|
|
145
|
+
entry.weekly_ops_risk_level !== undefined
|
|
146
|
+
? entry.weekly_ops_risk_level
|
|
147
|
+
: weeklyOpsSignals.risk
|
|
148
|
+
);
|
|
149
|
+
const weeklyOpsRiskLevel = weeklyOpsRiskRaw
|
|
150
|
+
? normalizeAutoHandoffGateRiskLevel(weeklyOpsRiskRaw)
|
|
151
|
+
: null;
|
|
152
|
+
const weeklyOpsGovernanceStatus = normalizeHandoffText(
|
|
153
|
+
entry.weekly_ops_governance_status !== undefined
|
|
154
|
+
? entry.weekly_ops_governance_status
|
|
155
|
+
: weeklyOpsSignals.governance_status
|
|
156
|
+
) || null;
|
|
157
|
+
const weeklyOpsAuthorizationTierBlockRatePercentCandidate = (
|
|
158
|
+
entry.weekly_ops_authorization_tier_block_rate_percent !== undefined
|
|
159
|
+
? entry.weekly_ops_authorization_tier_block_rate_percent
|
|
160
|
+
: weeklyOpsSignals.authorization_tier_block_rate_percent
|
|
161
|
+
);
|
|
162
|
+
const weeklyOpsDialogueAuthorizationBlockRatePercentCandidate = (
|
|
163
|
+
entry.weekly_ops_dialogue_authorization_block_rate_percent !== undefined
|
|
164
|
+
? entry.weekly_ops_dialogue_authorization_block_rate_percent
|
|
165
|
+
: weeklyOpsSignals.dialogue_authorization_block_rate_percent
|
|
166
|
+
);
|
|
167
|
+
const weeklyOpsMatrixRegressionPositiveRatePercentCandidate = (
|
|
168
|
+
entry.weekly_ops_matrix_regression_positive_rate_percent !== undefined
|
|
169
|
+
? entry.weekly_ops_matrix_regression_positive_rate_percent
|
|
170
|
+
: weeklyOpsSignals.matrix_regression_positive_rate_percent
|
|
171
|
+
);
|
|
172
|
+
const weeklyOpsRuntimeBlockRatePercentCandidate = (
|
|
173
|
+
entry.weekly_ops_runtime_block_rate_percent !== undefined
|
|
174
|
+
? entry.weekly_ops_runtime_block_rate_percent
|
|
175
|
+
: weeklyOpsSignals.runtime_block_rate_percent
|
|
176
|
+
);
|
|
177
|
+
const weeklyOpsRuntimeUiModeViolationTotalCandidate = (
|
|
178
|
+
entry.weekly_ops_runtime_ui_mode_violation_total !== undefined
|
|
179
|
+
? entry.weekly_ops_runtime_ui_mode_violation_total
|
|
180
|
+
: weeklyOpsSignals.runtime_ui_mode_violation_total
|
|
181
|
+
);
|
|
182
|
+
const weeklyOpsRuntimeUiModeViolationRatePercentCandidate = (
|
|
183
|
+
entry.weekly_ops_runtime_ui_mode_violation_rate_percent !== undefined
|
|
184
|
+
? entry.weekly_ops_runtime_ui_mode_violation_rate_percent
|
|
185
|
+
: weeklyOpsSignals.runtime_ui_mode_violation_rate_percent
|
|
186
|
+
);
|
|
187
|
+
const weeklyOpsViolationsCountCandidate = (
|
|
188
|
+
entry.weekly_ops_violations_count !== undefined
|
|
189
|
+
? entry.weekly_ops_violations_count
|
|
190
|
+
: (
|
|
191
|
+
weeklyOps.violations_count !== undefined
|
|
192
|
+
? weeklyOps.violations_count
|
|
193
|
+
: (weeklyOpsAvailable ? weeklyOpsViolations.length : null)
|
|
194
|
+
)
|
|
195
|
+
);
|
|
196
|
+
const weeklyOpsWarningCountCandidate = (
|
|
197
|
+
entry.weekly_ops_warning_count !== undefined
|
|
198
|
+
? entry.weekly_ops_warning_count
|
|
199
|
+
: (
|
|
200
|
+
weeklyOps.warning_count !== undefined
|
|
201
|
+
? weeklyOps.warning_count
|
|
202
|
+
: (weeklyOpsAvailable ? weeklyOpsWarnings.length : null)
|
|
203
|
+
)
|
|
204
|
+
);
|
|
205
|
+
const weeklyOpsConfigWarningCountCandidate = (
|
|
206
|
+
entry.weekly_ops_config_warning_count !== undefined
|
|
207
|
+
? entry.weekly_ops_config_warning_count
|
|
208
|
+
: (
|
|
209
|
+
weeklyOps.config_warning_count !== undefined
|
|
210
|
+
? weeklyOps.config_warning_count
|
|
211
|
+
: (weeklyOpsAvailable ? weeklyOpsConfigWarnings.length : null)
|
|
212
|
+
)
|
|
213
|
+
);
|
|
214
|
+
const weeklyOpsAuthorizationTierBlockRatePercent = (
|
|
215
|
+
weeklyOpsAuthorizationTierBlockRatePercentCandidate === null
|
|
216
|
+
|| weeklyOpsAuthorizationTierBlockRatePercentCandidate === undefined
|
|
217
|
+
|| weeklyOpsAuthorizationTierBlockRatePercentCandidate === ''
|
|
218
|
+
)
|
|
219
|
+
? null
|
|
220
|
+
: parseAutoHandoffGateNumber(weeklyOpsAuthorizationTierBlockRatePercentCandidate);
|
|
221
|
+
const weeklyOpsDialogueAuthorizationBlockRatePercent = (
|
|
222
|
+
weeklyOpsDialogueAuthorizationBlockRatePercentCandidate === null
|
|
223
|
+
|| weeklyOpsDialogueAuthorizationBlockRatePercentCandidate === undefined
|
|
224
|
+
|| weeklyOpsDialogueAuthorizationBlockRatePercentCandidate === ''
|
|
225
|
+
)
|
|
226
|
+
? null
|
|
227
|
+
: parseAutoHandoffGateNumber(weeklyOpsDialogueAuthorizationBlockRatePercentCandidate);
|
|
228
|
+
const weeklyOpsMatrixRegressionPositiveRatePercent = (
|
|
229
|
+
weeklyOpsMatrixRegressionPositiveRatePercentCandidate === null
|
|
230
|
+
|| weeklyOpsMatrixRegressionPositiveRatePercentCandidate === undefined
|
|
231
|
+
|| weeklyOpsMatrixRegressionPositiveRatePercentCandidate === ''
|
|
232
|
+
)
|
|
233
|
+
? null
|
|
234
|
+
: parseAutoHandoffGateNumber(weeklyOpsMatrixRegressionPositiveRatePercentCandidate);
|
|
235
|
+
const weeklyOpsRuntimeBlockRatePercent = (
|
|
236
|
+
weeklyOpsRuntimeBlockRatePercentCandidate === null
|
|
237
|
+
|| weeklyOpsRuntimeBlockRatePercentCandidate === undefined
|
|
238
|
+
|| weeklyOpsRuntimeBlockRatePercentCandidate === ''
|
|
239
|
+
)
|
|
240
|
+
? null
|
|
241
|
+
: parseAutoHandoffGateNumber(weeklyOpsRuntimeBlockRatePercentCandidate);
|
|
242
|
+
const weeklyOpsRuntimeUiModeViolationTotal = (
|
|
243
|
+
weeklyOpsRuntimeUiModeViolationTotalCandidate === null
|
|
244
|
+
|| weeklyOpsRuntimeUiModeViolationTotalCandidate === undefined
|
|
245
|
+
|| weeklyOpsRuntimeUiModeViolationTotalCandidate === ''
|
|
246
|
+
)
|
|
247
|
+
? null
|
|
248
|
+
: parseAutoHandoffGateNumber(weeklyOpsRuntimeUiModeViolationTotalCandidate);
|
|
249
|
+
const weeklyOpsRuntimeUiModeViolationRatePercent = (
|
|
250
|
+
weeklyOpsRuntimeUiModeViolationRatePercentCandidate === null
|
|
251
|
+
|| weeklyOpsRuntimeUiModeViolationRatePercentCandidate === undefined
|
|
252
|
+
|| weeklyOpsRuntimeUiModeViolationRatePercentCandidate === ''
|
|
253
|
+
)
|
|
254
|
+
? null
|
|
255
|
+
: parseAutoHandoffGateNumber(weeklyOpsRuntimeUiModeViolationRatePercentCandidate);
|
|
256
|
+
const weeklyOpsViolationsCount = (
|
|
257
|
+
weeklyOpsViolationsCountCandidate === null
|
|
258
|
+
|| weeklyOpsViolationsCountCandidate === undefined
|
|
259
|
+
|| weeklyOpsViolationsCountCandidate === ''
|
|
260
|
+
)
|
|
261
|
+
? null
|
|
262
|
+
: parseAutoHandoffGateNumber(weeklyOpsViolationsCountCandidate);
|
|
263
|
+
const weeklyOpsWarningCount = (
|
|
264
|
+
weeklyOpsWarningCountCandidate === null
|
|
265
|
+
|| weeklyOpsWarningCountCandidate === undefined
|
|
266
|
+
|| weeklyOpsWarningCountCandidate === ''
|
|
267
|
+
)
|
|
268
|
+
? null
|
|
269
|
+
: parseAutoHandoffGateNumber(weeklyOpsWarningCountCandidate);
|
|
270
|
+
const weeklyOpsConfigWarningCount = (
|
|
271
|
+
weeklyOpsConfigWarningCountCandidate === null
|
|
272
|
+
|| weeklyOpsConfigWarningCountCandidate === undefined
|
|
273
|
+
|| weeklyOpsConfigWarningCountCandidate === ''
|
|
274
|
+
)
|
|
275
|
+
? null
|
|
276
|
+
: parseAutoHandoffGateNumber(weeklyOpsConfigWarningCountCandidate);
|
|
277
|
+
const violations = Array.isArray(entry.violations)
|
|
278
|
+
? entry.violations.map(item => `${item}`)
|
|
279
|
+
: [];
|
|
280
|
+
const configWarnings = Array.isArray(entry.config_warnings)
|
|
281
|
+
? entry.config_warnings.map(item => `${item}`)
|
|
282
|
+
: [];
|
|
283
|
+
const signals = Array.isArray(entry.signals)
|
|
284
|
+
? entry.signals.map(item => `${item}`)
|
|
285
|
+
: [];
|
|
286
|
+
const thresholds = entry.thresholds && typeof entry.thresholds === 'object' && !Array.isArray(entry.thresholds)
|
|
287
|
+
? { ...entry.thresholds }
|
|
288
|
+
: {};
|
|
289
|
+
const evaluatedAt = normalizeHandoffText(
|
|
290
|
+
entry.evaluated_at || entry.generated_at || entry.updated_at
|
|
291
|
+
);
|
|
292
|
+
const mode = normalizeHandoffText(entry.mode);
|
|
293
|
+
const enforce = parseAutoHandoffGateBoolean(entry.enforce, false);
|
|
294
|
+
const evidenceUsed = parseAutoHandoffGateBoolean(entry.evidence_used, false);
|
|
295
|
+
const requireEvidence = parseAutoHandoffGateBoolean(entry.require_evidence, false);
|
|
296
|
+
const requireGatePass = parseAutoHandoffGateBoolean(entry.require_gate_pass, true);
|
|
297
|
+
const summaryFile = normalizeHandoffText(entry.summary_file);
|
|
298
|
+
const portableFile = sourceFile
|
|
299
|
+
? toPortablePath(projectPath, sourceFile)
|
|
300
|
+
: normalizeHandoffText(entry.file);
|
|
301
|
+
const violationsCount = Number.isInteger(entry.violations_count)
|
|
302
|
+
? entry.violations_count
|
|
303
|
+
: violations.length;
|
|
304
|
+
const configWarningCount = Number.isInteger(entry.config_warning_count)
|
|
305
|
+
? entry.config_warning_count
|
|
306
|
+
: configWarnings.length;
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
tag: derivedTag,
|
|
310
|
+
evaluated_at: evaluatedAt,
|
|
311
|
+
gate_passed: gatePassed,
|
|
312
|
+
mode,
|
|
313
|
+
enforce,
|
|
314
|
+
evidence_used: evidenceUsed,
|
|
315
|
+
require_evidence: requireEvidence,
|
|
316
|
+
require_gate_pass: requireGatePass,
|
|
317
|
+
risk_level: riskLevel,
|
|
318
|
+
spec_success_rate_percent: specSuccessRate,
|
|
319
|
+
scene_package_batch_status: sceneBatchStatus || null,
|
|
320
|
+
scene_package_batch_passed: typeof sceneBatchPassed === 'boolean' ? sceneBatchPassed : null,
|
|
321
|
+
scene_package_batch_failure_count: Number.isFinite(sceneBatchFailureCount) ? sceneBatchFailureCount : null,
|
|
322
|
+
capability_expected_unknown_count: Number.isFinite(capabilityExpectedUnknownCount)
|
|
323
|
+
? Math.max(0, Number(capabilityExpectedUnknownCount))
|
|
324
|
+
: null,
|
|
325
|
+
capability_provided_unknown_count: Number.isFinite(capabilityProvidedUnknownCount)
|
|
326
|
+
? Math.max(0, Number(capabilityProvidedUnknownCount))
|
|
327
|
+
: null,
|
|
328
|
+
release_gate_preflight_available: typeof releaseGatePreflightAvailable === 'boolean'
|
|
329
|
+
? releaseGatePreflightAvailable
|
|
330
|
+
: null,
|
|
331
|
+
release_gate_preflight_blocked: typeof releaseGatePreflightBlocked === 'boolean'
|
|
332
|
+
? releaseGatePreflightBlocked
|
|
333
|
+
: null,
|
|
334
|
+
require_release_gate_preflight: typeof requireReleaseGatePreflight === 'boolean'
|
|
335
|
+
? requireReleaseGatePreflight
|
|
336
|
+
: null,
|
|
337
|
+
drift_alert_count: Number.isFinite(driftAlertCount) ? Math.max(0, Number(driftAlertCount)) : null,
|
|
338
|
+
drift_blocked: typeof driftBlocked === 'boolean' ? driftBlocked : null,
|
|
339
|
+
drift_enforce: typeof driftEnforce === 'boolean' ? driftEnforce : null,
|
|
340
|
+
drift_evaluated_at: driftEvaluatedAt || null,
|
|
341
|
+
weekly_ops_available: weeklyOpsAvailable,
|
|
342
|
+
weekly_ops_blocked: typeof weeklyOpsBlocked === 'boolean' ? weeklyOpsBlocked : null,
|
|
343
|
+
weekly_ops_risk_level: weeklyOpsRiskLevel,
|
|
344
|
+
weekly_ops_governance_status: weeklyOpsGovernanceStatus,
|
|
345
|
+
weekly_ops_authorization_tier_block_rate_percent: Number.isFinite(weeklyOpsAuthorizationTierBlockRatePercent)
|
|
346
|
+
? weeklyOpsAuthorizationTierBlockRatePercent
|
|
347
|
+
: null,
|
|
348
|
+
weekly_ops_dialogue_authorization_block_rate_percent: Number.isFinite(weeklyOpsDialogueAuthorizationBlockRatePercent)
|
|
349
|
+
? weeklyOpsDialogueAuthorizationBlockRatePercent
|
|
350
|
+
: null,
|
|
351
|
+
weekly_ops_matrix_regression_positive_rate_percent: Number.isFinite(weeklyOpsMatrixRegressionPositiveRatePercent)
|
|
352
|
+
? weeklyOpsMatrixRegressionPositiveRatePercent
|
|
353
|
+
: null,
|
|
354
|
+
weekly_ops_runtime_block_rate_percent: Number.isFinite(weeklyOpsRuntimeBlockRatePercent)
|
|
355
|
+
? weeklyOpsRuntimeBlockRatePercent
|
|
356
|
+
: null,
|
|
357
|
+
weekly_ops_runtime_ui_mode_violation_total: Number.isFinite(weeklyOpsRuntimeUiModeViolationTotal)
|
|
358
|
+
? Math.max(0, Number(weeklyOpsRuntimeUiModeViolationTotal))
|
|
359
|
+
: null,
|
|
360
|
+
weekly_ops_runtime_ui_mode_violation_rate_percent: Number.isFinite(weeklyOpsRuntimeUiModeViolationRatePercent)
|
|
361
|
+
? Math.max(0, Number(weeklyOpsRuntimeUiModeViolationRatePercent))
|
|
362
|
+
: null,
|
|
363
|
+
weekly_ops_violations_count: Number.isFinite(weeklyOpsViolationsCount)
|
|
364
|
+
? Math.max(0, Number(weeklyOpsViolationsCount))
|
|
365
|
+
: null,
|
|
366
|
+
weekly_ops_warning_count: Number.isFinite(weeklyOpsWarningCount)
|
|
367
|
+
? Math.max(0, Number(weeklyOpsWarningCount))
|
|
368
|
+
: null,
|
|
369
|
+
weekly_ops_config_warning_count: Number.isFinite(weeklyOpsConfigWarningCount)
|
|
370
|
+
? Math.max(0, Number(weeklyOpsConfigWarningCount))
|
|
371
|
+
: null,
|
|
372
|
+
violations_count: Math.max(0, Number(violationsCount) || 0),
|
|
373
|
+
config_warning_count: Math.max(0, Number(configWarningCount) || 0),
|
|
374
|
+
thresholds,
|
|
375
|
+
summary_file: summaryFile,
|
|
376
|
+
file: portableFile,
|
|
377
|
+
signals,
|
|
378
|
+
violations,
|
|
379
|
+
config_warnings: configWarnings
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function loadAutoHandoffReleaseGateReports(projectPath, dirCandidate = null, dependencies = {}) {
|
|
384
|
+
const { resolveAutoHandoffReleaseEvidenceDir, fs, parseAutoHandoffReleaseGateTag, buildAutoHandoffReleaseGateHistoryEntry } = dependencies;
|
|
385
|
+
const dirPath = resolveAutoHandoffReleaseEvidenceDir(projectPath, dirCandidate);
|
|
386
|
+
const warnings = [];
|
|
387
|
+
if (!(await fs.pathExists(dirPath))) {
|
|
388
|
+
return {
|
|
389
|
+
dir: dirPath,
|
|
390
|
+
report_files: [],
|
|
391
|
+
entries: [],
|
|
392
|
+
warnings
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const names = await fs.readdir(dirPath);
|
|
397
|
+
const reportFiles = names
|
|
398
|
+
.filter(name => {
|
|
399
|
+
if (typeof name !== 'string') {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
const lowered = name.trim().toLowerCase();
|
|
403
|
+
if (!lowered.startsWith('release-gate-') || !lowered.endsWith('.json')) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
if (lowered === 'release-gate-history.json') {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
if (lowered.startsWith('release-gate-history-')) {
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
return parseAutoHandoffReleaseGateTag(name) !== null;
|
|
413
|
+
})
|
|
414
|
+
.map(name => path.join(dirPath, name));
|
|
415
|
+
|
|
416
|
+
const entries = [];
|
|
417
|
+
for (const reportFile of reportFiles) {
|
|
418
|
+
let payload = null;
|
|
419
|
+
try {
|
|
420
|
+
payload = await fs.readJson(reportFile);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
warnings.push(`skip invalid release gate report: ${reportFile} (${error.message})`);
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
426
|
+
warnings.push(`skip invalid release gate payload: ${reportFile}`);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
entries.push(buildAutoHandoffReleaseGateHistoryEntry(payload, { projectPath, file: reportFile, tag: parseAutoHandoffReleaseGateTag(path.basename(reportFile)) }, dependencies));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
dir: dirPath,
|
|
434
|
+
report_files: reportFiles,
|
|
435
|
+
entries,
|
|
436
|
+
warnings
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async function loadAutoHandoffReleaseGateHistorySeed(projectPath, fileCandidate = null, dependencies = {}) {
|
|
441
|
+
const { resolveAutoHandoffReleaseGateHistoryFile, fs, buildAutoHandoffReleaseGateHistoryEntry } = dependencies;
|
|
442
|
+
const filePath = resolveAutoHandoffReleaseGateHistoryFile(projectPath, fileCandidate);
|
|
443
|
+
if (!(await fs.pathExists(filePath))) {
|
|
444
|
+
return {
|
|
445
|
+
file: filePath,
|
|
446
|
+
entries: [],
|
|
447
|
+
warnings: []
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
let payload = null;
|
|
452
|
+
try {
|
|
453
|
+
payload = await fs.readJson(filePath);
|
|
454
|
+
} catch (error) {
|
|
455
|
+
return {
|
|
456
|
+
file: filePath,
|
|
457
|
+
entries: [],
|
|
458
|
+
warnings: [`skip invalid gate history file: ${filePath} (${error.message})`]
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
const list = Array.isArray(payload && payload.entries) ? payload.entries : [];
|
|
462
|
+
const entries = list
|
|
463
|
+
.filter(item => item && typeof item === 'object' && !Array.isArray(item))
|
|
464
|
+
.map(item => buildAutoHandoffReleaseGateHistoryEntry(item, { projectPath }, dependencies));
|
|
465
|
+
return {
|
|
466
|
+
file: filePath,
|
|
467
|
+
entries,
|
|
468
|
+
warnings: []
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function mergeAutoHandoffReleaseGateHistoryEntries(entries = [], dependencies = {}) {
|
|
473
|
+
const { normalizeHandoffText, toAutoHandoffTimestamp } = dependencies;
|
|
474
|
+
const merged = new Map();
|
|
475
|
+
entries.forEach((entry, index) => {
|
|
476
|
+
if (!entry || typeof entry !== 'object') {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const key = normalizeHandoffText(entry.tag)
|
|
480
|
+
|| normalizeHandoffText(entry.file)
|
|
481
|
+
|| `entry-${index}`;
|
|
482
|
+
const previous = merged.get(key);
|
|
483
|
+
if (!previous) {
|
|
484
|
+
merged.set(key, entry);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const prevTs = toAutoHandoffTimestamp(previous.evaluated_at);
|
|
488
|
+
const nextTs = toAutoHandoffTimestamp(entry.evaluated_at);
|
|
489
|
+
if (nextTs >= prevTs) {
|
|
490
|
+
merged.set(key, entry);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
return Array.from(merged.values());
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
module.exports = {
|
|
497
|
+
buildAutoHandoffReleaseGateHistoryEntry,
|
|
498
|
+
loadAutoHandoffReleaseGateReports,
|
|
499
|
+
loadAutoHandoffReleaseGateHistorySeed,
|
|
500
|
+
mergeAutoHandoffReleaseGateHistoryEntries
|
|
501
|
+
};
|
|
502
|
+
|