scene-capability-engine 3.6.64 → 3.6.67
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 +26 -0
- package/README.md +17 -6
- package/README.zh.md +18 -6
- package/bin/scene-capability-engine.js +4 -0
- package/docs/README.md +2 -2
- package/docs/command-reference.md +385 -8
- package/docs/document-governance.md +3 -2
- package/docs/integration-modes.md +62 -478
- package/docs/integration-philosophy.md +56 -263
- package/docs/magicball-cli-invocation-examples.md +1 -0
- package/docs/magicball-project-portfolio-contract.md +125 -4
- package/docs/project-management/README.md +14 -0
- package/docs/project-management/assurance/backup.md +3 -0
- package/docs/project-management/assurance/config.md +3 -0
- package/docs/project-management/assurance/evidence/README.md +3 -0
- package/docs/project-management/assurance/incidents/README.md +3 -0
- package/docs/project-management/assurance/logs.md +3 -0
- package/docs/project-management/assurance/overview.md +3 -0
- package/docs/project-management/assurance/recovery/README.md +3 -0
- package/docs/project-management/assurance/resource.md +3 -0
- package/docs/project-management/assurance/runbooks/README.md +3 -0
- package/docs/project-management/delivery/acceptance/README.md +3 -0
- package/docs/project-management/delivery/acceptance/evidence/README.md +3 -0
- package/docs/project-management/delivery/acceptance/exceptions/README.md +3 -0
- package/docs/project-management/delivery/acceptance/reports/README.md +3 -0
- package/docs/project-management/delivery/documents/changes.md +3 -0
- package/docs/project-management/delivery/documents/issues.md +3 -0
- package/docs/project-management/delivery/documents/overview.md +3 -0
- package/docs/project-management/delivery/documents/planning.md +3 -0
- package/docs/project-management/delivery/documents/requirements.md +3 -0
- package/docs/project-management/delivery/documents/tracking.md +3 -0
- package/docs/project-management/delivery/handoffs/README.md +3 -0
- package/docs/project-management/delivery/handoffs/evidence/README.md +3 -0
- package/docs/project-management/delivery/handoffs/records/README.md +3 -0
- package/docs/project-management/delivery/overview.md +10 -0
- package/docs/project-management/delivery/releases/README.md +3 -0
- package/docs/project-management/delivery/releases/baselines/README.md +3 -0
- package/docs/project-management/delivery/releases/evidence/README.md +3 -0
- package/docs/project-management/delivery/tables/changes.md +3 -0
- package/docs/project-management/delivery/tables/issues.md +3 -0
- package/docs/project-management/delivery/tables/planning.md +3 -0
- package/docs/project-management/delivery/tables/requirements.md +3 -0
- package/docs/project-management/delivery/tables/tracking.md +3 -0
- package/docs/project-management/environment/agent-discovery.md +3 -0
- package/docs/project-management/environment/development.md +3 -0
- package/docs/project-management/environment/overview.md +10 -0
- package/docs/project-management/environment/testing.md +3 -0
- package/docs/project-management/environment/version-alignment.md +3 -0
- package/docs/quick-start-with-ai-tools.md +68 -308
- package/docs/releases/README.md +3 -0
- package/docs/releases/v3.6.65.md +25 -0
- package/docs/releases/v3.6.66.md +23 -0
- package/docs/releases/v3.6.67.md +23 -0
- package/docs/steering-governance.md +64 -2
- package/docs/zh/README.md +2 -2
- package/docs/zh/releases/README.md +3 -0
- package/docs/zh/releases/v3.6.65.md +25 -0
- package/docs/zh/releases/v3.6.66.md +23 -0
- package/docs/zh/releases/v3.6.67.md +23 -0
- package/lib/commands/adopt.js +24 -0
- package/lib/commands/native.js +158 -0
- package/lib/commands/project.js +96 -0
- package/lib/commands/semantic.js +1459 -0
- package/lib/commands/session.js +74 -3
- package/lib/commands/spec-bootstrap.js +10 -1
- package/lib/commands/spec-gate.js +10 -1
- package/lib/commands/spec-pipeline.js +10 -1
- package/lib/commands/studio.js +405 -30
- package/lib/commands/task.js +141 -7
- package/lib/governance/supreme-principles.js +530 -0
- package/lib/problem/problem-evaluator.js +4 -0
- package/lib/project/candidate-inspection-service.js +24 -1
- package/lib/project/portfolio-projection-service.js +315 -5
- package/lib/project/project-channel-output.js +94 -0
- package/lib/project/project-channel-projection.js +181 -0
- package/lib/project/root-onboarding-service.js +107 -7
- package/lib/project/semantic-shared-source-projection.js +150 -0
- package/lib/project/supervision-action-model.js +277 -0
- package/lib/project/supervision-projection-service.js +305 -5
- package/lib/project/target-resolution-service.js +70 -5
- package/lib/project/visibility-policy.js +93 -0
- package/lib/runtime/multi-spec-scene-session.js +8 -1
- package/lib/runtime/project-channel-context-store.js +387 -0
- package/lib/runtime/project-channel-context.js +406 -0
- package/lib/runtime/scene-session-binding.js +46 -0
- package/lib/runtime/session-store.js +186 -0
- package/lib/runtime/steering-contract.js +7 -1
- package/lib/semantic/archive-report.js +283 -0
- package/lib/semantic/archive-routing.js +67 -0
- package/lib/semantic/backflow-report.js +245 -0
- package/lib/semantic/capability-contract.js +30 -0
- package/lib/semantic/delta-export.js +145 -0
- package/lib/semantic/interaction-observer.js +254 -0
- package/lib/semantic/kernel-loader.js +881 -0
- package/lib/semantic/native-runtime.js +359 -0
- package/lib/semantic/progress-ledger.js +433 -0
- package/lib/semantic/replay-evaluator.js +382 -0
- package/lib/semantic/shared-publication.js +592 -0
- package/lib/semantic/shared-source-config.js +183 -0
- package/lib/semantic/shared-source-connect.js +139 -0
- package/lib/semantic/shared-source-discovery.js +98 -0
- package/lib/semantic/shared-sync-export.js +413 -0
- package/lib/semantic/shared-sync-intake.js +592 -0
- package/lib/semantic/shared-sync-merge.js +547 -0
- package/lib/semantic/shared-sync-release.js +463 -0
- package/lib/semantic/supreme-intent-report.js +300 -0
- package/lib/state/sce-state-store.js +1360 -0
- package/lib/steering/context-sync-manager.js +276 -25
- package/lib/studio/spec-intake-governor.js +39 -3
- package/lib/studio/task-envelope.js +35 -2
- package/lib/workspace/takeover-baseline.js +342 -83
- package/package.json +7 -2
- package/scripts/agent-governance-baseline-audit.js +395 -0
- package/scripts/clarification-first-audit.js +9 -9
- package/scripts/deprecated-entry-audit.js +240 -0
- package/scripts/release-doc-version-audit.js +24 -0
- package/scripts/release-posture-report.js +262 -0
- package/template/.sce/README.md +62 -228
- package/template/.sce/config/semantic-shared-sources.json +5 -0
- package/template/.sce/config/supreme-principles-policy.json +105 -0
- package/template/.sce/config/takeover-baseline.json +7 -0
- package/template/.sce/steering/CORE_PRINCIPLES.md +23 -63
- package/template/.sce/steering/CURRENT_CONTEXT.md +4 -0
- package/template/.sce/steering/RULES_GUIDE.md +17 -9
- package/template/README.md +32 -96
|
@@ -0,0 +1,881 @@
|
|
|
1
|
+
const { getSceStateStore } = require('../state/sce-state-store');
|
|
2
|
+
const { publishSemanticLibraryDelta } = require('./shared-publication');
|
|
3
|
+
const {
|
|
4
|
+
resolveTargetLibrary,
|
|
5
|
+
buildCapabilityId
|
|
6
|
+
} = require('./capability-contract');
|
|
7
|
+
const { buildSemanticArchiveDecision } = require('./archive-routing');
|
|
8
|
+
|
|
9
|
+
function normalizeString(value) {
|
|
10
|
+
if (typeof value !== 'string') {
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
return value.trim();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function normalizeInteger(value, fallback = 0) {
|
|
17
|
+
const parsed = Number.parseInt(`${value}`, 10);
|
|
18
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
19
|
+
return fallback;
|
|
20
|
+
}
|
|
21
|
+
return parsed;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizeNonNegativeInteger(value, fallback = 0) {
|
|
25
|
+
const parsed = Number.parseInt(`${value}`, 10);
|
|
26
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
27
|
+
return fallback;
|
|
28
|
+
}
|
|
29
|
+
return parsed;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeStringArray(value, fallback = []) {
|
|
33
|
+
if (!Array.isArray(value)) {
|
|
34
|
+
return [...fallback];
|
|
35
|
+
}
|
|
36
|
+
return value
|
|
37
|
+
.map((item) => normalizeString(item))
|
|
38
|
+
.filter(Boolean);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function unique(values = []) {
|
|
42
|
+
return Array.from(new Set(normalizeStringArray(values, [])));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function clamp(value, min, max) {
|
|
46
|
+
return Math.min(Math.max(value, min), max);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function tokenize(value) {
|
|
50
|
+
const normalized = normalizeString(value).toLowerCase();
|
|
51
|
+
if (!normalized) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
return (normalized.match(/[a-z0-9\u4e00-\u9fff]+/g) || [])
|
|
55
|
+
.filter((item) => item.length > 1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isPromotedSemanticStatus(status) {
|
|
59
|
+
const normalized = normalizeString(status).toLowerCase();
|
|
60
|
+
return normalized === 'promoted'
|
|
61
|
+
|| normalized === 'promoted-local'
|
|
62
|
+
|| normalized === 'published-shared'
|
|
63
|
+
|| normalized === 'publish_pending';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function computeActivation(prompt, lesson = {}) {
|
|
67
|
+
const promptTokens = [...new Set(tokenize(prompt))];
|
|
68
|
+
const lessonBasis = [
|
|
69
|
+
lesson.title,
|
|
70
|
+
lesson.summary,
|
|
71
|
+
lesson.heuristic,
|
|
72
|
+
lesson.lesson_type,
|
|
73
|
+
lesson.source_runtime,
|
|
74
|
+
lesson.fingerprint,
|
|
75
|
+
JSON.stringify(lesson.evidence || {}),
|
|
76
|
+
JSON.stringify(lesson.metadata || {})
|
|
77
|
+
].join(' ');
|
|
78
|
+
const lessonTokens = [...new Set(tokenize(lessonBasis))];
|
|
79
|
+
const matchedTerms = promptTokens.filter((token) => lessonTokens.includes(token));
|
|
80
|
+
return {
|
|
81
|
+
lesson_id: lesson.lesson_id,
|
|
82
|
+
score: matchedTerms.length,
|
|
83
|
+
matched_terms: matchedTerms
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getStateStore(projectPath, dependencies = {}) {
|
|
88
|
+
return dependencies.stateStore || getSceStateStore(projectPath, {
|
|
89
|
+
fileSystem: dependencies.fileSystem,
|
|
90
|
+
env: dependencies.env,
|
|
91
|
+
sqliteModule: dependencies.sqliteModule,
|
|
92
|
+
noCache: dependencies.noCache === true
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function readLessonPublishMetadata(lesson = {}) {
|
|
97
|
+
if (lesson.metadata && typeof lesson.metadata === 'object'
|
|
98
|
+
&& lesson.metadata.publish
|
|
99
|
+
&& typeof lesson.metadata.publish === 'object') {
|
|
100
|
+
return lesson.metadata.publish;
|
|
101
|
+
}
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function extractObservationIds(lesson = {}, evaluationRun = {}, existingLedger = {}) {
|
|
106
|
+
const evidence = lesson.evidence && typeof lesson.evidence === 'object' ? lesson.evidence : {};
|
|
107
|
+
const lineage = existingLedger.lineage && typeof existingLedger.lineage === 'object'
|
|
108
|
+
? existingLedger.lineage
|
|
109
|
+
: {};
|
|
110
|
+
return unique([
|
|
111
|
+
...(Array.isArray(lineage.observation_ids) ? lineage.observation_ids : []),
|
|
112
|
+
...normalizeStringArray(evidence.observation_ids, []),
|
|
113
|
+
normalizeString(evidence.observation_id),
|
|
114
|
+
...normalizeStringArray(evaluationRun.observation_ids, [])
|
|
115
|
+
]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function mapLessonStatusToCapabilityStatus(status) {
|
|
119
|
+
const normalized = normalizeString(status).toLowerCase();
|
|
120
|
+
if (normalized === 'deprecated') {
|
|
121
|
+
return 'deprecated';
|
|
122
|
+
}
|
|
123
|
+
if (normalized === 'publish_blocked') {
|
|
124
|
+
return 'quarantined';
|
|
125
|
+
}
|
|
126
|
+
if (normalized === 'candidate') {
|
|
127
|
+
return 'candidate';
|
|
128
|
+
}
|
|
129
|
+
return 'active';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function normalizeQualitySignal(value) {
|
|
133
|
+
const normalized = normalizeString(value).toLowerCase();
|
|
134
|
+
if (['positive', 'negative', 'mixed', 'unknown'].includes(normalized)) {
|
|
135
|
+
return normalized;
|
|
136
|
+
}
|
|
137
|
+
return 'mixed';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function readLessonMetadata(lesson = {}) {
|
|
141
|
+
return lesson.metadata && typeof lesson.metadata === 'object'
|
|
142
|
+
? lesson.metadata
|
|
143
|
+
: {};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function readLessonChannelId(lesson = {}) {
|
|
147
|
+
const metadata = readLessonMetadata(lesson);
|
|
148
|
+
return normalizeString(metadata.channel_id || metadata.channelId) || null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function readLessonSessionId(lesson = {}) {
|
|
152
|
+
const metadata = readLessonMetadata(lesson);
|
|
153
|
+
return normalizeString(metadata.session_id || metadata.sessionId) || null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function calculateGovernancePenaltyScore(metrics = {}) {
|
|
157
|
+
const rewriteCount = normalizeNonNegativeInteger(metrics.governance_rewrite_count, 0);
|
|
158
|
+
const clarifyCount = normalizeNonNegativeInteger(metrics.governance_clarify_count, 0);
|
|
159
|
+
const narrowCount = normalizeNonNegativeInteger(metrics.governance_narrow_count, 0);
|
|
160
|
+
const refuseCount = normalizeNonNegativeInteger(metrics.governance_refuse_count, 0);
|
|
161
|
+
const planningBlocked = normalizeNonNegativeInteger(metrics.governance_planning_blocked_count, 0);
|
|
162
|
+
const executionBlocked = normalizeNonNegativeInteger(metrics.governance_execution_blocked_count, 0);
|
|
163
|
+
const governedRate = Number(metrics.governance_governed_rate_percent);
|
|
164
|
+
|
|
165
|
+
let penalty = 0;
|
|
166
|
+
penalty += rewriteCount * 3;
|
|
167
|
+
penalty += clarifyCount * 2;
|
|
168
|
+
penalty += narrowCount * 1;
|
|
169
|
+
penalty += refuseCount * 8;
|
|
170
|
+
penalty += planningBlocked * 4;
|
|
171
|
+
penalty += executionBlocked * 8;
|
|
172
|
+
if (Number.isFinite(governedRate)) {
|
|
173
|
+
penalty += governedRate / 20;
|
|
174
|
+
}
|
|
175
|
+
return Number(clamp(penalty, 0, 40).toFixed(2));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function summarizeGovernancePressure(scope = {}, store, limit = 200) {
|
|
179
|
+
if (!store) {
|
|
180
|
+
return {
|
|
181
|
+
governance_scope: {
|
|
182
|
+
project_id: null,
|
|
183
|
+
channel_id: null,
|
|
184
|
+
scene_id: null,
|
|
185
|
+
spec_id: null,
|
|
186
|
+
limit
|
|
187
|
+
},
|
|
188
|
+
governance_total_assessments: 0,
|
|
189
|
+
governance_governed_count: 0,
|
|
190
|
+
governance_clarify_count: 0,
|
|
191
|
+
governance_rewrite_count: 0,
|
|
192
|
+
governance_narrow_count: 0,
|
|
193
|
+
governance_refuse_count: 0,
|
|
194
|
+
governance_planning_blocked_count: 0,
|
|
195
|
+
governance_execution_blocked_count: 0,
|
|
196
|
+
governance_governed_rate_percent: 0,
|
|
197
|
+
governance_penalty_score: 0
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const projectId = normalizeString(scope.project_id || scope.projectId) || null;
|
|
202
|
+
const channelId = normalizeString(scope.channel_id || scope.channelId) || null;
|
|
203
|
+
const sceneId = normalizeString(scope.scene_id || scope.sceneId) || null;
|
|
204
|
+
const specId = normalizeString(scope.spec_id || scope.specId) || null;
|
|
205
|
+
const assessments = await store.listSupremeIntentAssessments({
|
|
206
|
+
project_id: projectId,
|
|
207
|
+
channel_id: channelId,
|
|
208
|
+
scene_id: sceneId,
|
|
209
|
+
spec_id: specId,
|
|
210
|
+
limit
|
|
211
|
+
});
|
|
212
|
+
const items = Array.isArray(assessments) ? assessments : [];
|
|
213
|
+
const summarizeAction = (action) => items.filter((item) => normalizeString(item.action) === action).length;
|
|
214
|
+
const governedCount = items.filter((item) => normalizeString(item.action) !== 'allow').length;
|
|
215
|
+
const planningBlockedCount = items.filter((item) => item.planning_allowed !== true).length;
|
|
216
|
+
const executionBlockedCount = items.filter((item) => item.execution_allowed !== true).length;
|
|
217
|
+
const metrics = {
|
|
218
|
+
governance_scope: {
|
|
219
|
+
project_id: projectId,
|
|
220
|
+
channel_id: channelId,
|
|
221
|
+
scene_id: sceneId,
|
|
222
|
+
spec_id: specId,
|
|
223
|
+
limit
|
|
224
|
+
},
|
|
225
|
+
governance_total_assessments: items.length,
|
|
226
|
+
governance_governed_count: governedCount,
|
|
227
|
+
governance_clarify_count: summarizeAction('clarify'),
|
|
228
|
+
governance_rewrite_count: summarizeAction('rewrite'),
|
|
229
|
+
governance_narrow_count: summarizeAction('narrow'),
|
|
230
|
+
governance_refuse_count: summarizeAction('refuse'),
|
|
231
|
+
governance_planning_blocked_count: planningBlockedCount,
|
|
232
|
+
governance_execution_blocked_count: executionBlockedCount,
|
|
233
|
+
governance_governed_rate_percent: items.length > 0
|
|
234
|
+
? Number(((governedCount / items.length) * 100).toFixed(2))
|
|
235
|
+
: 0
|
|
236
|
+
};
|
|
237
|
+
return {
|
|
238
|
+
...metrics,
|
|
239
|
+
governance_penalty_score: calculateGovernancePenaltyScore(metrics)
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function calculateRankScore(options = {}) {
|
|
244
|
+
const confidence = Number(options.confidence);
|
|
245
|
+
const evaluationScore = Number(options.evaluation_score ?? options.evaluationScore);
|
|
246
|
+
const activationSuccessCount = normalizeNonNegativeInteger(options.activation_success_count ?? options.activationSuccessCount, 0);
|
|
247
|
+
const activationMixedCount = normalizeNonNegativeInteger(options.activation_mixed_count ?? options.activationMixedCount, 0);
|
|
248
|
+
const activationNegativeCount = normalizeNonNegativeInteger(options.activation_negative_count ?? options.activationNegativeCount, 0);
|
|
249
|
+
const quarantineCount = normalizeNonNegativeInteger(options.quarantine_count ?? options.quarantineCount, 0);
|
|
250
|
+
const governancePenaltyScore = Number.isFinite(Number(
|
|
251
|
+
options.governance_penalty_score ?? options.governancePenaltyScore
|
|
252
|
+
))
|
|
253
|
+
? Number(options.governance_penalty_score ?? options.governancePenaltyScore)
|
|
254
|
+
: calculateGovernancePenaltyScore(options);
|
|
255
|
+
|
|
256
|
+
let score = 40;
|
|
257
|
+
if (Number.isFinite(confidence)) {
|
|
258
|
+
score += confidence * 20;
|
|
259
|
+
}
|
|
260
|
+
if (Number.isFinite(evaluationScore)) {
|
|
261
|
+
score += evaluationScore / 10;
|
|
262
|
+
}
|
|
263
|
+
score += activationSuccessCount * 12;
|
|
264
|
+
score += activationMixedCount * 3;
|
|
265
|
+
score -= activationNegativeCount * 15;
|
|
266
|
+
score -= quarantineCount * 20;
|
|
267
|
+
score -= governancePenaltyScore;
|
|
268
|
+
return Number(clamp(score, 0, 100).toFixed(2));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function shouldQuarantineCapability(metrics = {}) {
|
|
272
|
+
const negativeCount = normalizeNonNegativeInteger(metrics.activation_negative_count, 0);
|
|
273
|
+
const successCount = normalizeNonNegativeInteger(metrics.activation_success_count, 0);
|
|
274
|
+
return negativeCount >= 2 && negativeCount > successCount;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async function syncSemanticCapabilityLedger(lesson = {}, options = {}, dependencies = {}) {
|
|
278
|
+
const lessonId = normalizeString(lesson.lesson_id || options.lesson_id || options.lessonId);
|
|
279
|
+
if (!lessonId) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
284
|
+
const store = getStateStore(projectPath, dependencies);
|
|
285
|
+
const evaluationRun = options.evaluationRun && typeof options.evaluationRun === 'object'
|
|
286
|
+
? options.evaluationRun
|
|
287
|
+
: null;
|
|
288
|
+
const capabilityId = normalizeString(options.capability_id || options.capabilityId) || buildCapabilityId(lesson);
|
|
289
|
+
const existingLedger = await store.getSemanticCapabilityLedger(capabilityId);
|
|
290
|
+
const publish = readLessonPublishMetadata(lesson);
|
|
291
|
+
const nowIso = normalizeString(options.updated_at || options.updatedAt) || new Date().toISOString();
|
|
292
|
+
const archive = buildSemanticArchiveDecision(lesson);
|
|
293
|
+
const targetLibrary = normalizeString(options.target_library || options.targetLibrary)
|
|
294
|
+
|| normalizeString(archive.target_library)
|
|
295
|
+
|| resolveTargetLibrary(lesson);
|
|
296
|
+
const capabilityStatus = normalizeString(options.capability_status || options.capabilityStatus)
|
|
297
|
+
|| mapLessonStatusToCapabilityStatus(lesson.status);
|
|
298
|
+
const publishState = normalizeString(options.publish_state || options.publishState || publish.publish_state)
|
|
299
|
+
|| normalizeString(existingLedger && existingLedger.publish_state)
|
|
300
|
+
|| 'pending';
|
|
301
|
+
const governanceMetrics = await summarizeGovernancePressure({
|
|
302
|
+
project_id: normalizeString(lesson.project_id) || normalizeString(existingLedger && existingLedger.project_id),
|
|
303
|
+
channel_id: normalizeString(options.channel_id || options.channelId)
|
|
304
|
+
|| readLessonChannelId(lesson)
|
|
305
|
+
|| normalizeString(existingLedger && existingLedger.channel_id),
|
|
306
|
+
scene_id: normalizeString(lesson.scene_id) || normalizeString(existingLedger && existingLedger.scene_id),
|
|
307
|
+
spec_id: normalizeString(lesson.spec_id) || normalizeString(existingLedger && existingLedger.spec_id)
|
|
308
|
+
}, store);
|
|
309
|
+
|
|
310
|
+
const metrics = {
|
|
311
|
+
...(existingLedger && existingLedger.metrics && typeof existingLedger.metrics === 'object'
|
|
312
|
+
? existingLedger.metrics
|
|
313
|
+
: {}),
|
|
314
|
+
confidence: Number.isFinite(Number(lesson.confidence)) ? Number(lesson.confidence) : null,
|
|
315
|
+
evaluation_score: evaluationRun && Number.isFinite(Number(evaluationRun.score))
|
|
316
|
+
? Number(evaluationRun.score)
|
|
317
|
+
: Number(existingLedger && existingLedger.metrics && existingLedger.metrics.evaluation_score),
|
|
318
|
+
promotion_release_gate: normalizeString(options.release_gate || options.releaseGate)
|
|
319
|
+
|| normalizeString(evaluationRun && evaluationRun.release_gate)
|
|
320
|
+
|| normalizeString(existingLedger && existingLedger.metrics && existingLedger.metrics.promotion_release_gate)
|
|
321
|
+
|| null,
|
|
322
|
+
activation_count: normalizeNonNegativeInteger(
|
|
323
|
+
options.activation_count ?? options.activationCount ?? (existingLedger && existingLedger.activation_count),
|
|
324
|
+
0
|
|
325
|
+
),
|
|
326
|
+
activation_success_count: normalizeNonNegativeInteger(
|
|
327
|
+
options.activation_success_count ?? options.activationSuccessCount ?? (existingLedger && existingLedger.activation_success_count),
|
|
328
|
+
0
|
|
329
|
+
),
|
|
330
|
+
activation_mixed_count: normalizeNonNegativeInteger(
|
|
331
|
+
options.activation_mixed_count ?? options.activationMixedCount ?? (existingLedger && existingLedger.activation_mixed_count),
|
|
332
|
+
0
|
|
333
|
+
),
|
|
334
|
+
activation_negative_count: normalizeNonNegativeInteger(
|
|
335
|
+
options.activation_negative_count ?? options.activationNegativeCount ?? (existingLedger && existingLedger.activation_negative_count),
|
|
336
|
+
0
|
|
337
|
+
),
|
|
338
|
+
quarantine_count: normalizeNonNegativeInteger(
|
|
339
|
+
options.quarantine_count ?? options.quarantineCount ?? (existingLedger && existingLedger.quarantine_count),
|
|
340
|
+
0
|
|
341
|
+
),
|
|
342
|
+
last_activated_at: normalizeString(options.last_activated_at || options.lastActivatedAt)
|
|
343
|
+
|| normalizeString(existingLedger && existingLedger.last_activated_at)
|
|
344
|
+
|| null,
|
|
345
|
+
last_quality_signal: normalizeString(options.last_quality_signal || options.lastQualitySignal)
|
|
346
|
+
|| normalizeString(existingLedger && existingLedger.last_quality_signal)
|
|
347
|
+
|| null,
|
|
348
|
+
last_runtime: normalizeString(options.last_runtime || options.lastRuntime)
|
|
349
|
+
|| normalizeString(existingLedger && existingLedger.last_runtime)
|
|
350
|
+
|| null,
|
|
351
|
+
archive_class: normalizeString(archive.archive_class)
|
|
352
|
+
|| normalizeString(existingLedger && existingLedger.metrics && existingLedger.metrics.archive_class)
|
|
353
|
+
|| null,
|
|
354
|
+
archive_reuse_scope: normalizeString(archive.reuse_scope)
|
|
355
|
+
|| normalizeString(existingLedger && existingLedger.metrics && existingLedger.metrics.archive_reuse_scope)
|
|
356
|
+
|| null,
|
|
357
|
+
archive_reason_code: normalizeString(archive.reason_code)
|
|
358
|
+
|| normalizeString(existingLedger && existingLedger.metrics && existingLedger.metrics.archive_reason_code)
|
|
359
|
+
|| null,
|
|
360
|
+
...governanceMetrics
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const publication = {
|
|
364
|
+
...(existingLedger && existingLedger.publication && typeof existingLedger.publication === 'object'
|
|
365
|
+
? existingLedger.publication
|
|
366
|
+
: {}),
|
|
367
|
+
...(publish && typeof publish === 'object' ? publish : {}),
|
|
368
|
+
consent_model: normalizeString(publish.consent_model)
|
|
369
|
+
|| normalizeString(existingLedger && existingLedger.publication && existingLedger.publication.consent_model)
|
|
370
|
+
|| 'adopt-implied',
|
|
371
|
+
publish_required: publish.publish_required === true
|
|
372
|
+
|| Boolean(existingLedger && existingLedger.publication && existingLedger.publication.publish_required),
|
|
373
|
+
publish_state: publishState,
|
|
374
|
+
queued_at: normalizeString(publish.queued_at)
|
|
375
|
+
|| normalizeString(existingLedger && existingLedger.publication && existingLedger.publication.queued_at)
|
|
376
|
+
|| null,
|
|
377
|
+
target_library: targetLibrary,
|
|
378
|
+
archive,
|
|
379
|
+
publish_target_libraries: unique([
|
|
380
|
+
...(Array.isArray(publish.publish_target_libraries) ? publish.publish_target_libraries : []),
|
|
381
|
+
...(Array.isArray(existingLedger && existingLedger.publication && existingLedger.publication.publish_target_libraries)
|
|
382
|
+
? existingLedger.publication.publish_target_libraries
|
|
383
|
+
: []),
|
|
384
|
+
targetLibrary
|
|
385
|
+
])
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const lineage = {
|
|
389
|
+
...(existingLedger && existingLedger.lineage && typeof existingLedger.lineage === 'object'
|
|
390
|
+
? existingLedger.lineage
|
|
391
|
+
: {}),
|
|
392
|
+
lesson_id: lessonId,
|
|
393
|
+
fingerprint: normalizeString(lesson.fingerprint)
|
|
394
|
+
|| normalizeString(existingLedger && existingLedger.lineage && existingLedger.lineage.fingerprint)
|
|
395
|
+
|| null,
|
|
396
|
+
observation_ids: extractObservationIds(lesson, evaluationRun || {}, existingLedger || {}),
|
|
397
|
+
evaluation_run_id: normalizeString(options.evaluation_run_id || options.evaluationRunId)
|
|
398
|
+
|| normalizeString(evaluationRun && evaluationRun.evaluation_run_id)
|
|
399
|
+
|| normalizeString(existingLedger && existingLedger.evaluation_run_id)
|
|
400
|
+
|| null,
|
|
401
|
+
source_runtime: normalizeString(lesson.source_runtime)
|
|
402
|
+
|| normalizeString(existingLedger && existingLedger.lineage && existingLedger.lineage.source_runtime)
|
|
403
|
+
|| null,
|
|
404
|
+
target_library: targetLibrary,
|
|
405
|
+
archive
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const rankScore = calculateRankScore({
|
|
409
|
+
confidence: metrics.confidence,
|
|
410
|
+
evaluation_score: metrics.evaluation_score,
|
|
411
|
+
activation_success_count: metrics.activation_success_count,
|
|
412
|
+
activation_mixed_count: metrics.activation_mixed_count,
|
|
413
|
+
activation_negative_count: metrics.activation_negative_count,
|
|
414
|
+
quarantine_count: metrics.quarantine_count,
|
|
415
|
+
governance_penalty_score: metrics.governance_penalty_score
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
return store.upsertSemanticCapabilityLedger({
|
|
419
|
+
capability_id: capabilityId,
|
|
420
|
+
lesson_id: lessonId,
|
|
421
|
+
evaluation_run_id: lineage.evaluation_run_id,
|
|
422
|
+
project_id: normalizeString(lesson.project_id) || normalizeString(existingLedger && existingLedger.project_id) || null,
|
|
423
|
+
channel_id: normalizeString(options.channel_id || options.channelId)
|
|
424
|
+
|| readLessonChannelId(lesson)
|
|
425
|
+
|| normalizeString(existingLedger && existingLedger.channel_id)
|
|
426
|
+
|| null,
|
|
427
|
+
session_id: normalizeString(options.session_id || options.sessionId)
|
|
428
|
+
|| readLessonSessionId(lesson)
|
|
429
|
+
|| normalizeString(existingLedger && existingLedger.session_id)
|
|
430
|
+
|| null,
|
|
431
|
+
scene_id: normalizeString(lesson.scene_id) || normalizeString(existingLedger && existingLedger.scene_id) || null,
|
|
432
|
+
spec_id: normalizeString(lesson.spec_id) || normalizeString(existingLedger && existingLedger.spec_id) || null,
|
|
433
|
+
target_library: targetLibrary,
|
|
434
|
+
capability_status: capabilityStatus,
|
|
435
|
+
publish_state: publishState,
|
|
436
|
+
activation_count: metrics.activation_count,
|
|
437
|
+
activation_success_count: metrics.activation_success_count,
|
|
438
|
+
activation_mixed_count: metrics.activation_mixed_count,
|
|
439
|
+
activation_negative_count: metrics.activation_negative_count,
|
|
440
|
+
quarantine_count: metrics.quarantine_count,
|
|
441
|
+
last_activated_at: metrics.last_activated_at,
|
|
442
|
+
last_quality_signal: metrics.last_quality_signal,
|
|
443
|
+
last_runtime: metrics.last_runtime,
|
|
444
|
+
rank_score: rankScore,
|
|
445
|
+
lineage,
|
|
446
|
+
publication,
|
|
447
|
+
metrics,
|
|
448
|
+
created_at: normalizeString(existingLedger && existingLedger.created_at) || nowIso,
|
|
449
|
+
updated_at: nowIso
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async function applySemanticCapabilityQuarantine(lesson = {}, options = {}, dependencies = {}) {
|
|
454
|
+
const lessonId = normalizeString(lesson.lesson_id || options.lesson_id || options.lessonId);
|
|
455
|
+
if (!lessonId) {
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
460
|
+
const store = getStateStore(projectPath, dependencies);
|
|
461
|
+
const existingLesson = lesson.lesson_id ? lesson : await store.getSemanticLessonCandidate(lessonId);
|
|
462
|
+
if (!existingLesson) {
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const nowIso = normalizeString(options.updated_at || options.updatedAt) || new Date().toISOString();
|
|
467
|
+
const reason = normalizeString(options.reason) || 'native-quality-regression';
|
|
468
|
+
const actor = normalizeString(options.actor) || 'sce-native';
|
|
469
|
+
const existingPublish = readLessonPublishMetadata(existingLesson);
|
|
470
|
+
|
|
471
|
+
return store.upsertSemanticLessonCandidate({
|
|
472
|
+
...existingLesson,
|
|
473
|
+
status: normalizeString(existingLesson.status) === 'deprecated' ? 'deprecated' : 'publish_blocked',
|
|
474
|
+
metadata: {
|
|
475
|
+
...(existingLesson.metadata || {}),
|
|
476
|
+
publish: {
|
|
477
|
+
...existingPublish,
|
|
478
|
+
consent_model: normalizeString(existingPublish.consent_model) || 'adopt-implied',
|
|
479
|
+
publish_required: existingPublish.publish_required === true,
|
|
480
|
+
publish_state: 'blocked',
|
|
481
|
+
blocked_at: nowIso,
|
|
482
|
+
blocked_reason: reason
|
|
483
|
+
},
|
|
484
|
+
quarantine: {
|
|
485
|
+
quarantined_at: nowIso,
|
|
486
|
+
quarantined_by: actor,
|
|
487
|
+
reason
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
updated_at: nowIso
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async function listPromotedSemanticBehaviors(options = {}, dependencies = {}) {
|
|
495
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
496
|
+
const store = getStateStore(projectPath, dependencies);
|
|
497
|
+
const projectId = normalizeString(options.project_id || options.projectId);
|
|
498
|
+
const channelId = normalizeString(options.channel_id || options.channelId);
|
|
499
|
+
const sceneId = normalizeString(options.scene_id || options.sceneId);
|
|
500
|
+
const specId = normalizeString(options.spec_id || options.specId);
|
|
501
|
+
const limit = normalizeInteger(options.limit, 20);
|
|
502
|
+
|
|
503
|
+
let items = await store.listSemanticLessonCandidates({
|
|
504
|
+
project_id: projectId,
|
|
505
|
+
limit: limit > 0 ? Math.max(limit * 4, 20) : 20
|
|
506
|
+
});
|
|
507
|
+
items = Array.isArray(items) ? items : [];
|
|
508
|
+
items = items.filter((item) => {
|
|
509
|
+
if (!isPromotedSemanticStatus(item.status)) {
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
if (sceneId && normalizeString(item.scene_id) !== sceneId) {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
if (specId && normalizeString(item.spec_id) !== specId) {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
if (channelId && readLessonChannelId(item) !== channelId) {
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
return true;
|
|
522
|
+
});
|
|
523
|
+
if (limit > 0) {
|
|
524
|
+
items = items.slice(0, limit);
|
|
525
|
+
}
|
|
526
|
+
return items;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
async function activatePromotedSemanticBehaviors(prompt, options = {}, dependencies = {}) {
|
|
530
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
531
|
+
const store = getStateStore(projectPath, dependencies);
|
|
532
|
+
const behaviors = await listPromotedSemanticBehaviors(options, {
|
|
533
|
+
...dependencies,
|
|
534
|
+
projectPath,
|
|
535
|
+
stateStore: store
|
|
536
|
+
});
|
|
537
|
+
const ranked = await Promise.all(behaviors.map(async (item) => {
|
|
538
|
+
const capabilityId = buildCapabilityId(item);
|
|
539
|
+
const ledger = await store.getSemanticCapabilityLedger(capabilityId);
|
|
540
|
+
return {
|
|
541
|
+
lesson: item,
|
|
542
|
+
capability_id: capabilityId,
|
|
543
|
+
ledger,
|
|
544
|
+
activation: computeActivation(prompt, item)
|
|
545
|
+
};
|
|
546
|
+
}));
|
|
547
|
+
|
|
548
|
+
return ranked
|
|
549
|
+
.map((item) => ({
|
|
550
|
+
...item,
|
|
551
|
+
activation: {
|
|
552
|
+
...item.activation,
|
|
553
|
+
weighted_score: Number((
|
|
554
|
+
Number(item.activation.score || 0)
|
|
555
|
+
+ ((Number(item.ledger && item.ledger.rank_score) || 0) / 25)
|
|
556
|
+
).toFixed(2))
|
|
557
|
+
}
|
|
558
|
+
}))
|
|
559
|
+
.filter((item) => item.activation.score > 0)
|
|
560
|
+
.sort((left, right) => {
|
|
561
|
+
if (right.activation.weighted_score !== left.activation.weighted_score) {
|
|
562
|
+
return right.activation.weighted_score - left.activation.weighted_score;
|
|
563
|
+
}
|
|
564
|
+
if (right.activation.score !== left.activation.score) {
|
|
565
|
+
return right.activation.score - left.activation.score;
|
|
566
|
+
}
|
|
567
|
+
const leftRank = Number(left.ledger && left.ledger.rank_score) || 0;
|
|
568
|
+
const rightRank = Number(right.ledger && right.ledger.rank_score) || 0;
|
|
569
|
+
if (rightRank !== leftRank) {
|
|
570
|
+
return rightRank - leftRank;
|
|
571
|
+
}
|
|
572
|
+
return `${left.lesson.lesson_id}`.localeCompare(`${right.lesson.lesson_id}`);
|
|
573
|
+
})
|
|
574
|
+
.map((item) => ({
|
|
575
|
+
...item.lesson,
|
|
576
|
+
capability_id: item.capability_id,
|
|
577
|
+
rank_score: Number(item.ledger && item.ledger.rank_score) || 0,
|
|
578
|
+
activation: item.activation
|
|
579
|
+
}));
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
async function promoteSemanticLessonCandidate(options = {}, dependencies = {}) {
|
|
583
|
+
const lessonId = normalizeString(options.lesson_id || options.lessonId);
|
|
584
|
+
const evaluationRunId = normalizeString(options.evaluation_run_id || options.evaluationRunId);
|
|
585
|
+
if (!lessonId) {
|
|
586
|
+
throw new Error('lesson_id is required');
|
|
587
|
+
}
|
|
588
|
+
if (!evaluationRunId) {
|
|
589
|
+
throw new Error('evaluation_run_id is required');
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
593
|
+
const store = getStateStore(projectPath, dependencies);
|
|
594
|
+
|
|
595
|
+
const lesson = await store.getSemanticLessonCandidate(lessonId);
|
|
596
|
+
if (!lesson) {
|
|
597
|
+
throw new Error(`Semantic lesson candidate not found: ${lessonId}`);
|
|
598
|
+
}
|
|
599
|
+
const evaluationRun = await store.getSemanticEvaluationRun(evaluationRunId);
|
|
600
|
+
if (!evaluationRun) {
|
|
601
|
+
throw new Error(`Semantic evaluation run not found: ${evaluationRunId}`);
|
|
602
|
+
}
|
|
603
|
+
if (normalizeString(evaluationRun.verdict) !== 'passed') {
|
|
604
|
+
throw new Error(`Evaluation run ${evaluationRunId} did not pass`);
|
|
605
|
+
}
|
|
606
|
+
if (evaluationRun.regression_detected === true) {
|
|
607
|
+
throw new Error(`Evaluation run ${evaluationRunId} is marked as regression`);
|
|
608
|
+
}
|
|
609
|
+
if (evaluationRun.governance && evaluationRun.governance.promotion_gate_passed === false) {
|
|
610
|
+
throw new Error(`Evaluation run ${evaluationRunId} did not pass promotion gate`);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const nowIso = new Date().toISOString();
|
|
614
|
+
const actor = normalizeString(options.actor) || null;
|
|
615
|
+
const releaseGate = normalizeString(options.release_gate || options.releaseGate)
|
|
616
|
+
|| normalizeString(evaluationRun.release_gate)
|
|
617
|
+
|| null;
|
|
618
|
+
const archive = buildSemanticArchiveDecision(lesson);
|
|
619
|
+
|
|
620
|
+
const promotedLesson = await store.upsertSemanticLessonCandidate({
|
|
621
|
+
...lesson,
|
|
622
|
+
status: 'publish_pending',
|
|
623
|
+
evidence: {
|
|
624
|
+
...(lesson.evidence || {}),
|
|
625
|
+
promotion: {
|
|
626
|
+
evaluation_run_id: evaluationRun.evaluation_run_id,
|
|
627
|
+
score: evaluationRun.score,
|
|
628
|
+
threshold: evaluationRun.threshold,
|
|
629
|
+
expected_terms: evaluationRun.evidence
|
|
630
|
+
&& evaluationRun.evidence.replay_dataset
|
|
631
|
+
&& Array.isArray(evaluationRun.evidence.replay_dataset.expected_terms)
|
|
632
|
+
? evaluationRun.evidence.replay_dataset.expected_terms
|
|
633
|
+
: [],
|
|
634
|
+
matched_terms: evaluationRun.evidence && Array.isArray(evaluationRun.evidence.matched_terms)
|
|
635
|
+
? evaluationRun.evidence.matched_terms
|
|
636
|
+
: []
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
metadata: {
|
|
640
|
+
...(lesson.metadata || {}),
|
|
641
|
+
promotion: {
|
|
642
|
+
promoted_at: nowIso,
|
|
643
|
+
promoted_by: actor,
|
|
644
|
+
release_gate: releaseGate
|
|
645
|
+
},
|
|
646
|
+
archive,
|
|
647
|
+
publish: {
|
|
648
|
+
consent_model: 'adopt-implied',
|
|
649
|
+
publish_required: true,
|
|
650
|
+
publish_state: 'pending',
|
|
651
|
+
publish_target_libraries: [archive.target_library],
|
|
652
|
+
queued_at: nowIso
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
updated_at: nowIso
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
await syncSemanticCapabilityLedger(promotedLesson, {
|
|
659
|
+
evaluationRun,
|
|
660
|
+
release_gate: releaseGate,
|
|
661
|
+
capability_status: 'active',
|
|
662
|
+
publish_state: 'pending',
|
|
663
|
+
updated_at: nowIso
|
|
664
|
+
}, {
|
|
665
|
+
...dependencies,
|
|
666
|
+
projectPath,
|
|
667
|
+
stateStore: store
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
if (dependencies.autoPublish !== false) {
|
|
671
|
+
const publication = await publishSemanticLibraryDelta({
|
|
672
|
+
project_id: promotedLesson.project_id,
|
|
673
|
+
spec_id: promotedLesson.spec_id,
|
|
674
|
+
lesson_id: promotedLesson.lesson_id,
|
|
675
|
+
capability_id: buildCapabilityId(promotedLesson),
|
|
676
|
+
limit: 1
|
|
677
|
+
}, {
|
|
678
|
+
...dependencies,
|
|
679
|
+
projectPath,
|
|
680
|
+
fileSystem: dependencies.fileSystem,
|
|
681
|
+
stateStore: store,
|
|
682
|
+
syncSemanticCapabilityLedger
|
|
683
|
+
});
|
|
684
|
+
if (!publication || !publication.totals) {
|
|
685
|
+
throw new Error(`Automatic semantic publication failed for lesson ${promotedLesson.lesson_id}`);
|
|
686
|
+
}
|
|
687
|
+
if (publication.totals.published < 1 && publication.totals.blocked < 1) {
|
|
688
|
+
throw new Error(`Automatic semantic publication failed for lesson ${promotedLesson.lesson_id}`);
|
|
689
|
+
}
|
|
690
|
+
const latestLesson = await store.getSemanticLessonCandidate(promotedLesson.lesson_id);
|
|
691
|
+
return {
|
|
692
|
+
...latestLesson,
|
|
693
|
+
auto_publication: publication
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
return promotedLesson;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async function rollbackPromotedSemanticLesson(options = {}, dependencies = {}) {
|
|
701
|
+
const lessonId = normalizeString(options.lesson_id || options.lessonId);
|
|
702
|
+
if (!lessonId) {
|
|
703
|
+
throw new Error('lesson_id is required');
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
707
|
+
const store = getStateStore(projectPath, dependencies);
|
|
708
|
+
|
|
709
|
+
const lesson = await store.getSemanticLessonCandidate(lessonId);
|
|
710
|
+
if (!lesson) {
|
|
711
|
+
throw new Error(`Semantic lesson candidate not found: ${lessonId}`);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const nowIso = new Date().toISOString();
|
|
715
|
+
const actor = normalizeString(options.actor) || null;
|
|
716
|
+
const reason = normalizeString(options.reason) || 'manual-rollback';
|
|
717
|
+
const publish = readLessonPublishMetadata(lesson);
|
|
718
|
+
const rolledBackLesson = await store.upsertSemanticLessonCandidate({
|
|
719
|
+
...lesson,
|
|
720
|
+
status: 'deprecated',
|
|
721
|
+
metadata: {
|
|
722
|
+
...(lesson.metadata || {}),
|
|
723
|
+
publish: {
|
|
724
|
+
...publish,
|
|
725
|
+
consent_model: normalizeString(publish.consent_model) || 'adopt-implied',
|
|
726
|
+
publish_required: publish.publish_required === true,
|
|
727
|
+
publish_state: 'blocked',
|
|
728
|
+
blocked_at: nowIso,
|
|
729
|
+
blocked_reason: reason
|
|
730
|
+
},
|
|
731
|
+
rollback: {
|
|
732
|
+
rolled_back_at: nowIso,
|
|
733
|
+
rolled_back_by: actor,
|
|
734
|
+
reason
|
|
735
|
+
}
|
|
736
|
+
},
|
|
737
|
+
updated_at: nowIso
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
await syncSemanticCapabilityLedger(rolledBackLesson, {
|
|
741
|
+
capability_status: 'deprecated',
|
|
742
|
+
publish_state: 'blocked',
|
|
743
|
+
updated_at: nowIso
|
|
744
|
+
}, {
|
|
745
|
+
...dependencies,
|
|
746
|
+
projectPath,
|
|
747
|
+
stateStore: store
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
return rolledBackLesson;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
async function recordSemanticCapabilityActivation(options = {}, dependencies = {}) {
|
|
754
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
755
|
+
const store = getStateStore(projectPath, dependencies);
|
|
756
|
+
let lesson = options.lesson && typeof options.lesson === 'object'
|
|
757
|
+
? options.lesson
|
|
758
|
+
: null;
|
|
759
|
+
|
|
760
|
+
if (!lesson) {
|
|
761
|
+
const lessonId = normalizeString(options.lesson_id || options.lessonId);
|
|
762
|
+
if (lessonId) {
|
|
763
|
+
lesson = await store.getSemanticLessonCandidate(lessonId);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const capabilityId = normalizeString(options.capability_id || options.capabilityId)
|
|
768
|
+
|| (lesson ? buildCapabilityId(lesson) : '');
|
|
769
|
+
if (!capabilityId) {
|
|
770
|
+
throw new Error('capability_id or lesson_id is required');
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
let ledger = await store.getSemanticCapabilityLedger(capabilityId);
|
|
774
|
+
if (!ledger && lesson) {
|
|
775
|
+
ledger = await syncSemanticCapabilityLedger(lesson, {}, {
|
|
776
|
+
...dependencies,
|
|
777
|
+
projectPath,
|
|
778
|
+
stateStore: store
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
if (!ledger) {
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (!lesson && ledger.lesson_id) {
|
|
786
|
+
lesson = await store.getSemanticLessonCandidate(ledger.lesson_id);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
const qualitySignal = normalizeQualitySignal(options.quality_signal || options.qualitySignal);
|
|
790
|
+
const nowIso = normalizeString(options.activated_at || options.activatedAt) || new Date().toISOString();
|
|
791
|
+
const activationCount = normalizeNonNegativeInteger(ledger.activation_count, 0) + 1;
|
|
792
|
+
const activationSuccessCount = normalizeNonNegativeInteger(ledger.activation_success_count, 0) + (qualitySignal === 'positive' ? 1 : 0);
|
|
793
|
+
const activationMixedCount = normalizeNonNegativeInteger(ledger.activation_mixed_count, 0) + (qualitySignal === 'mixed' || qualitySignal === 'unknown' ? 1 : 0);
|
|
794
|
+
const activationNegativeCount = normalizeNonNegativeInteger(ledger.activation_negative_count, 0) + (qualitySignal === 'negative' ? 1 : 0);
|
|
795
|
+
const baseQuarantineCount = normalizeNonNegativeInteger(ledger.quarantine_count, 0);
|
|
796
|
+
const nextMetrics = {
|
|
797
|
+
...(ledger.metrics || {}),
|
|
798
|
+
confidence: lesson && Number.isFinite(Number(lesson.confidence)) ? Number(lesson.confidence) : ledger.metrics && ledger.metrics.confidence,
|
|
799
|
+
evaluation_score: ledger.metrics && Number.isFinite(Number(ledger.metrics.evaluation_score))
|
|
800
|
+
? Number(ledger.metrics.evaluation_score)
|
|
801
|
+
: null,
|
|
802
|
+
promotion_release_gate: normalizeString(ledger.metrics && ledger.metrics.promotion_release_gate) || null,
|
|
803
|
+
activation_count: activationCount,
|
|
804
|
+
activation_success_count: activationSuccessCount,
|
|
805
|
+
activation_mixed_count: activationMixedCount,
|
|
806
|
+
activation_negative_count: activationNegativeCount,
|
|
807
|
+
quarantine_count: baseQuarantineCount,
|
|
808
|
+
last_activated_at: nowIso,
|
|
809
|
+
last_quality_signal: qualitySignal,
|
|
810
|
+
last_runtime: normalizeString(options.source_runtime || options.sourceRuntime) || 'sce-native'
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
const willQuarantine = shouldQuarantineCapability(nextMetrics);
|
|
814
|
+
if (willQuarantine && normalizeString(ledger.capability_status) !== 'quarantined') {
|
|
815
|
+
nextMetrics.quarantine_count = baseQuarantineCount + 1;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
const updatedLedger = await syncSemanticCapabilityLedger(lesson || {
|
|
819
|
+
lesson_id: ledger.lesson_id,
|
|
820
|
+
project_id: ledger.project_id,
|
|
821
|
+
scene_id: ledger.scene_id,
|
|
822
|
+
spec_id: ledger.spec_id,
|
|
823
|
+
source_runtime: ledger.lineage && ledger.lineage.source_runtime
|
|
824
|
+
}, {
|
|
825
|
+
capability_id: capabilityId,
|
|
826
|
+
evaluation_run_id: ledger.evaluation_run_id,
|
|
827
|
+
channel_id: normalizeString(options.channel_id || options.channelId) || ledger.channel_id,
|
|
828
|
+
session_id: normalizeString(options.session_id || options.sessionId) || ledger.session_id,
|
|
829
|
+
capability_status: willQuarantine ? 'quarantined' : normalizeString(ledger.capability_status) || 'active',
|
|
830
|
+
publish_state: willQuarantine ? 'blocked' : normalizeString(ledger.publish_state) || 'pending',
|
|
831
|
+
activation_count: nextMetrics.activation_count,
|
|
832
|
+
activation_success_count: nextMetrics.activation_success_count,
|
|
833
|
+
activation_mixed_count: nextMetrics.activation_mixed_count,
|
|
834
|
+
activation_negative_count: nextMetrics.activation_negative_count,
|
|
835
|
+
quarantine_count: nextMetrics.quarantine_count,
|
|
836
|
+
last_activated_at: nowIso,
|
|
837
|
+
last_quality_signal: qualitySignal,
|
|
838
|
+
last_runtime: nextMetrics.last_runtime,
|
|
839
|
+
updated_at: nowIso
|
|
840
|
+
}, {
|
|
841
|
+
...dependencies,
|
|
842
|
+
projectPath,
|
|
843
|
+
stateStore: store
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
let updatedLesson = lesson;
|
|
847
|
+
if (willQuarantine && lesson) {
|
|
848
|
+
updatedLesson = await applySemanticCapabilityQuarantine(lesson, {
|
|
849
|
+
actor: normalizeString(options.actor) || 'sce-native',
|
|
850
|
+
reason: normalizeString(options.reason) || 'native-quality-regression',
|
|
851
|
+
updated_at: nowIso
|
|
852
|
+
}, {
|
|
853
|
+
...dependencies,
|
|
854
|
+
projectPath,
|
|
855
|
+
stateStore: store
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
return {
|
|
860
|
+
ledger: updatedLedger,
|
|
861
|
+
lesson: updatedLesson,
|
|
862
|
+
quarantined: willQuarantine
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
module.exports = {
|
|
867
|
+
isPromotedSemanticStatus,
|
|
868
|
+
resolveTargetLibrary,
|
|
869
|
+
buildCapabilityId,
|
|
870
|
+
calculateRankScore,
|
|
871
|
+
calculateGovernancePenaltyScore,
|
|
872
|
+
shouldQuarantineCapability,
|
|
873
|
+
summarizeGovernancePressure,
|
|
874
|
+
syncSemanticCapabilityLedger,
|
|
875
|
+
applySemanticCapabilityQuarantine,
|
|
876
|
+
listPromotedSemanticBehaviors,
|
|
877
|
+
activatePromotedSemanticBehaviors,
|
|
878
|
+
promoteSemanticLessonCandidate,
|
|
879
|
+
rollbackPromotedSemanticLesson,
|
|
880
|
+
recordSemanticCapabilityActivation
|
|
881
|
+
};
|