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.
Files changed (125) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +17 -6
  3. package/README.zh.md +18 -6
  4. package/bin/scene-capability-engine.js +4 -0
  5. package/docs/README.md +2 -2
  6. package/docs/command-reference.md +385 -8
  7. package/docs/document-governance.md +3 -2
  8. package/docs/integration-modes.md +62 -478
  9. package/docs/integration-philosophy.md +56 -263
  10. package/docs/magicball-cli-invocation-examples.md +1 -0
  11. package/docs/magicball-project-portfolio-contract.md +125 -4
  12. package/docs/project-management/README.md +14 -0
  13. package/docs/project-management/assurance/backup.md +3 -0
  14. package/docs/project-management/assurance/config.md +3 -0
  15. package/docs/project-management/assurance/evidence/README.md +3 -0
  16. package/docs/project-management/assurance/incidents/README.md +3 -0
  17. package/docs/project-management/assurance/logs.md +3 -0
  18. package/docs/project-management/assurance/overview.md +3 -0
  19. package/docs/project-management/assurance/recovery/README.md +3 -0
  20. package/docs/project-management/assurance/resource.md +3 -0
  21. package/docs/project-management/assurance/runbooks/README.md +3 -0
  22. package/docs/project-management/delivery/acceptance/README.md +3 -0
  23. package/docs/project-management/delivery/acceptance/evidence/README.md +3 -0
  24. package/docs/project-management/delivery/acceptance/exceptions/README.md +3 -0
  25. package/docs/project-management/delivery/acceptance/reports/README.md +3 -0
  26. package/docs/project-management/delivery/documents/changes.md +3 -0
  27. package/docs/project-management/delivery/documents/issues.md +3 -0
  28. package/docs/project-management/delivery/documents/overview.md +3 -0
  29. package/docs/project-management/delivery/documents/planning.md +3 -0
  30. package/docs/project-management/delivery/documents/requirements.md +3 -0
  31. package/docs/project-management/delivery/documents/tracking.md +3 -0
  32. package/docs/project-management/delivery/handoffs/README.md +3 -0
  33. package/docs/project-management/delivery/handoffs/evidence/README.md +3 -0
  34. package/docs/project-management/delivery/handoffs/records/README.md +3 -0
  35. package/docs/project-management/delivery/overview.md +10 -0
  36. package/docs/project-management/delivery/releases/README.md +3 -0
  37. package/docs/project-management/delivery/releases/baselines/README.md +3 -0
  38. package/docs/project-management/delivery/releases/evidence/README.md +3 -0
  39. package/docs/project-management/delivery/tables/changes.md +3 -0
  40. package/docs/project-management/delivery/tables/issues.md +3 -0
  41. package/docs/project-management/delivery/tables/planning.md +3 -0
  42. package/docs/project-management/delivery/tables/requirements.md +3 -0
  43. package/docs/project-management/delivery/tables/tracking.md +3 -0
  44. package/docs/project-management/environment/agent-discovery.md +3 -0
  45. package/docs/project-management/environment/development.md +3 -0
  46. package/docs/project-management/environment/overview.md +10 -0
  47. package/docs/project-management/environment/testing.md +3 -0
  48. package/docs/project-management/environment/version-alignment.md +3 -0
  49. package/docs/quick-start-with-ai-tools.md +68 -308
  50. package/docs/releases/README.md +3 -0
  51. package/docs/releases/v3.6.65.md +25 -0
  52. package/docs/releases/v3.6.66.md +23 -0
  53. package/docs/releases/v3.6.67.md +23 -0
  54. package/docs/steering-governance.md +64 -2
  55. package/docs/zh/README.md +2 -2
  56. package/docs/zh/releases/README.md +3 -0
  57. package/docs/zh/releases/v3.6.65.md +25 -0
  58. package/docs/zh/releases/v3.6.66.md +23 -0
  59. package/docs/zh/releases/v3.6.67.md +23 -0
  60. package/lib/commands/adopt.js +24 -0
  61. package/lib/commands/native.js +158 -0
  62. package/lib/commands/project.js +96 -0
  63. package/lib/commands/semantic.js +1459 -0
  64. package/lib/commands/session.js +74 -3
  65. package/lib/commands/spec-bootstrap.js +10 -1
  66. package/lib/commands/spec-gate.js +10 -1
  67. package/lib/commands/spec-pipeline.js +10 -1
  68. package/lib/commands/studio.js +405 -30
  69. package/lib/commands/task.js +141 -7
  70. package/lib/governance/supreme-principles.js +530 -0
  71. package/lib/problem/problem-evaluator.js +4 -0
  72. package/lib/project/candidate-inspection-service.js +24 -1
  73. package/lib/project/portfolio-projection-service.js +315 -5
  74. package/lib/project/project-channel-output.js +94 -0
  75. package/lib/project/project-channel-projection.js +181 -0
  76. package/lib/project/root-onboarding-service.js +107 -7
  77. package/lib/project/semantic-shared-source-projection.js +150 -0
  78. package/lib/project/supervision-action-model.js +277 -0
  79. package/lib/project/supervision-projection-service.js +305 -5
  80. package/lib/project/target-resolution-service.js +70 -5
  81. package/lib/project/visibility-policy.js +93 -0
  82. package/lib/runtime/multi-spec-scene-session.js +8 -1
  83. package/lib/runtime/project-channel-context-store.js +387 -0
  84. package/lib/runtime/project-channel-context.js +406 -0
  85. package/lib/runtime/scene-session-binding.js +46 -0
  86. package/lib/runtime/session-store.js +186 -0
  87. package/lib/runtime/steering-contract.js +7 -1
  88. package/lib/semantic/archive-report.js +283 -0
  89. package/lib/semantic/archive-routing.js +67 -0
  90. package/lib/semantic/backflow-report.js +245 -0
  91. package/lib/semantic/capability-contract.js +30 -0
  92. package/lib/semantic/delta-export.js +145 -0
  93. package/lib/semantic/interaction-observer.js +254 -0
  94. package/lib/semantic/kernel-loader.js +881 -0
  95. package/lib/semantic/native-runtime.js +359 -0
  96. package/lib/semantic/progress-ledger.js +433 -0
  97. package/lib/semantic/replay-evaluator.js +382 -0
  98. package/lib/semantic/shared-publication.js +592 -0
  99. package/lib/semantic/shared-source-config.js +183 -0
  100. package/lib/semantic/shared-source-connect.js +139 -0
  101. package/lib/semantic/shared-source-discovery.js +98 -0
  102. package/lib/semantic/shared-sync-export.js +413 -0
  103. package/lib/semantic/shared-sync-intake.js +592 -0
  104. package/lib/semantic/shared-sync-merge.js +547 -0
  105. package/lib/semantic/shared-sync-release.js +463 -0
  106. package/lib/semantic/supreme-intent-report.js +300 -0
  107. package/lib/state/sce-state-store.js +1360 -0
  108. package/lib/steering/context-sync-manager.js +276 -25
  109. package/lib/studio/spec-intake-governor.js +39 -3
  110. package/lib/studio/task-envelope.js +35 -2
  111. package/lib/workspace/takeover-baseline.js +342 -83
  112. package/package.json +7 -2
  113. package/scripts/agent-governance-baseline-audit.js +395 -0
  114. package/scripts/clarification-first-audit.js +9 -9
  115. package/scripts/deprecated-entry-audit.js +240 -0
  116. package/scripts/release-doc-version-audit.js +24 -0
  117. package/scripts/release-posture-report.js +262 -0
  118. package/template/.sce/README.md +62 -228
  119. package/template/.sce/config/semantic-shared-sources.json +5 -0
  120. package/template/.sce/config/supreme-principles-policy.json +105 -0
  121. package/template/.sce/config/takeover-baseline.json +7 -0
  122. package/template/.sce/steering/CORE_PRINCIPLES.md +23 -63
  123. package/template/.sce/steering/CURRENT_CONTEXT.md +4 -0
  124. package/template/.sce/steering/RULES_GUIDE.md +17 -9
  125. 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
+ };