scene-capability-engine 3.6.65 → 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 (121) hide show
  1. package/CHANGELOG.md +16 -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 +382 -6
  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-project-portfolio-contract.md +114 -2
  11. package/docs/project-management/README.md +14 -0
  12. package/docs/project-management/assurance/backup.md +3 -0
  13. package/docs/project-management/assurance/config.md +3 -0
  14. package/docs/project-management/assurance/evidence/README.md +3 -0
  15. package/docs/project-management/assurance/incidents/README.md +3 -0
  16. package/docs/project-management/assurance/logs.md +3 -0
  17. package/docs/project-management/assurance/overview.md +3 -0
  18. package/docs/project-management/assurance/recovery/README.md +3 -0
  19. package/docs/project-management/assurance/resource.md +3 -0
  20. package/docs/project-management/assurance/runbooks/README.md +3 -0
  21. package/docs/project-management/delivery/acceptance/README.md +3 -0
  22. package/docs/project-management/delivery/acceptance/evidence/README.md +3 -0
  23. package/docs/project-management/delivery/acceptance/exceptions/README.md +3 -0
  24. package/docs/project-management/delivery/acceptance/reports/README.md +3 -0
  25. package/docs/project-management/delivery/documents/changes.md +3 -0
  26. package/docs/project-management/delivery/documents/issues.md +3 -0
  27. package/docs/project-management/delivery/documents/overview.md +3 -0
  28. package/docs/project-management/delivery/documents/planning.md +3 -0
  29. package/docs/project-management/delivery/documents/requirements.md +3 -0
  30. package/docs/project-management/delivery/documents/tracking.md +3 -0
  31. package/docs/project-management/delivery/handoffs/README.md +3 -0
  32. package/docs/project-management/delivery/handoffs/evidence/README.md +3 -0
  33. package/docs/project-management/delivery/handoffs/records/README.md +3 -0
  34. package/docs/project-management/delivery/overview.md +10 -0
  35. package/docs/project-management/delivery/releases/README.md +3 -0
  36. package/docs/project-management/delivery/releases/baselines/README.md +3 -0
  37. package/docs/project-management/delivery/releases/evidence/README.md +3 -0
  38. package/docs/project-management/delivery/tables/changes.md +3 -0
  39. package/docs/project-management/delivery/tables/issues.md +3 -0
  40. package/docs/project-management/delivery/tables/planning.md +3 -0
  41. package/docs/project-management/delivery/tables/requirements.md +3 -0
  42. package/docs/project-management/delivery/tables/tracking.md +3 -0
  43. package/docs/project-management/environment/agent-discovery.md +3 -0
  44. package/docs/project-management/environment/development.md +3 -0
  45. package/docs/project-management/environment/overview.md +10 -0
  46. package/docs/project-management/environment/testing.md +3 -0
  47. package/docs/project-management/environment/version-alignment.md +3 -0
  48. package/docs/quick-start-with-ai-tools.md +68 -308
  49. package/docs/releases/README.md +2 -0
  50. package/docs/releases/v3.6.66.md +23 -0
  51. package/docs/releases/v3.6.67.md +23 -0
  52. package/docs/steering-governance.md +64 -2
  53. package/docs/zh/README.md +2 -2
  54. package/docs/zh/releases/README.md +2 -0
  55. package/docs/zh/releases/v3.6.66.md +23 -0
  56. package/docs/zh/releases/v3.6.67.md +23 -0
  57. package/lib/commands/adopt.js +24 -0
  58. package/lib/commands/native.js +158 -0
  59. package/lib/commands/project.js +95 -0
  60. package/lib/commands/semantic.js +1459 -0
  61. package/lib/commands/session.js +74 -3
  62. package/lib/commands/spec-bootstrap.js +10 -1
  63. package/lib/commands/spec-gate.js +10 -1
  64. package/lib/commands/spec-pipeline.js +10 -1
  65. package/lib/commands/studio.js +405 -30
  66. package/lib/commands/task.js +141 -7
  67. package/lib/governance/supreme-principles.js +530 -0
  68. package/lib/problem/problem-evaluator.js +4 -0
  69. package/lib/project/candidate-inspection-service.js +24 -1
  70. package/lib/project/portfolio-projection-service.js +315 -5
  71. package/lib/project/project-channel-output.js +94 -0
  72. package/lib/project/project-channel-projection.js +181 -0
  73. package/lib/project/root-onboarding-service.js +60 -8
  74. package/lib/project/semantic-shared-source-projection.js +150 -0
  75. package/lib/project/supervision-action-model.js +277 -0
  76. package/lib/project/supervision-projection-service.js +305 -5
  77. package/lib/project/target-resolution-service.js +70 -5
  78. package/lib/project/visibility-policy.js +93 -0
  79. package/lib/runtime/multi-spec-scene-session.js +8 -1
  80. package/lib/runtime/project-channel-context-store.js +387 -0
  81. package/lib/runtime/project-channel-context.js +406 -0
  82. package/lib/runtime/scene-session-binding.js +46 -0
  83. package/lib/runtime/session-store.js +186 -0
  84. package/lib/runtime/steering-contract.js +7 -1
  85. package/lib/semantic/archive-report.js +283 -0
  86. package/lib/semantic/archive-routing.js +67 -0
  87. package/lib/semantic/backflow-report.js +245 -0
  88. package/lib/semantic/capability-contract.js +30 -0
  89. package/lib/semantic/delta-export.js +145 -0
  90. package/lib/semantic/interaction-observer.js +254 -0
  91. package/lib/semantic/kernel-loader.js +881 -0
  92. package/lib/semantic/native-runtime.js +359 -0
  93. package/lib/semantic/progress-ledger.js +433 -0
  94. package/lib/semantic/replay-evaluator.js +382 -0
  95. package/lib/semantic/shared-publication.js +592 -0
  96. package/lib/semantic/shared-source-config.js +183 -0
  97. package/lib/semantic/shared-source-connect.js +139 -0
  98. package/lib/semantic/shared-source-discovery.js +98 -0
  99. package/lib/semantic/shared-sync-export.js +413 -0
  100. package/lib/semantic/shared-sync-intake.js +592 -0
  101. package/lib/semantic/shared-sync-merge.js +547 -0
  102. package/lib/semantic/shared-sync-release.js +463 -0
  103. package/lib/semantic/supreme-intent-report.js +300 -0
  104. package/lib/state/sce-state-store.js +1360 -0
  105. package/lib/steering/context-sync-manager.js +276 -25
  106. package/lib/studio/spec-intake-governor.js +39 -3
  107. package/lib/studio/task-envelope.js +35 -2
  108. package/lib/workspace/takeover-baseline.js +342 -83
  109. package/package.json +7 -2
  110. package/scripts/agent-governance-baseline-audit.js +395 -0
  111. package/scripts/clarification-first-audit.js +9 -9
  112. package/scripts/deprecated-entry-audit.js +240 -0
  113. package/scripts/release-posture-report.js +262 -0
  114. package/template/.sce/README.md +62 -228
  115. package/template/.sce/config/semantic-shared-sources.json +5 -0
  116. package/template/.sce/config/supreme-principles-policy.json +105 -0
  117. package/template/.sce/config/takeover-baseline.json +7 -0
  118. package/template/.sce/steering/CORE_PRINCIPLES.md +23 -63
  119. package/template/.sce/steering/CURRENT_CONTEXT.md +4 -0
  120. package/template/.sce/steering/RULES_GUIDE.md +17 -9
  121. package/template/README.md +32 -96
@@ -0,0 +1,592 @@
1
+ const path = require('path');
2
+ const crypto = require('crypto');
3
+ const fs = require('fs-extra');
4
+ const { getSceStateStore } = require('../state/sce-state-store');
5
+ const { exportSemanticLibraryDelta } = require('./delta-export');
6
+
7
+ const SHARED_LIBRARY_ROOT = '.sce/knowledge/semantic-shared';
8
+ const SHARED_LIBRARY_INDEX_API_VERSION = 'sce.semantic.shared-library.index/v0.1';
9
+ const SHARED_LIBRARY_ENTRY_API_VERSION = 'sce.semantic.shared-library.entry/v0.1';
10
+ const SHARED_LIBRARY_ROOT_INDEX_API_VERSION = 'sce.semantic.shared-library.root-index/v0.1';
11
+ const SEMANTIC_PUBLICATION_REPORT_API_VERSION = 'sce.semantic.publication-report/v0.1';
12
+ const GOVERNED_PUBLICATION_MAX_PENALTY_SCORE = 20;
13
+ const SUPPORTED_TARGET_LIBRARIES = new Set([
14
+ 'experience-library',
15
+ 'ontology-capability-library',
16
+ 'application-library'
17
+ ]);
18
+
19
+ function normalizeString(value) {
20
+ if (typeof value !== 'string') {
21
+ return '';
22
+ }
23
+ return value.trim();
24
+ }
25
+
26
+ function normalizeInteger(value, fallback = 0) {
27
+ const parsed = Number.parseInt(`${value}`, 10);
28
+ if (!Number.isFinite(parsed) || parsed <= 0) {
29
+ return fallback;
30
+ }
31
+ return parsed;
32
+ }
33
+
34
+ function isObject(value) {
35
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
36
+ }
37
+
38
+ function summarizeArchiveMetrics(archive = {}) {
39
+ if (!isObject(archive)) {
40
+ return {};
41
+ }
42
+ return {
43
+ archive_class: normalizeString(archive.archive_class) || null,
44
+ archive_reuse_scope: normalizeString(archive.reuse_scope) || null,
45
+ archive_reason_code: normalizeString(archive.reason_code) || null
46
+ };
47
+ }
48
+
49
+ async function readJsonIfExists(fileSystem, absolutePath, fallback) {
50
+ if (!await fileSystem.pathExists(absolutePath)) {
51
+ return fallback;
52
+ }
53
+ try {
54
+ return await fileSystem.readJson(absolutePath);
55
+ } catch (_error) {
56
+ return fallback;
57
+ }
58
+ }
59
+
60
+ function toProjectRelative(projectPath, targetPath) {
61
+ return path.relative(projectPath, targetPath).replace(/\\/g, '/');
62
+ }
63
+
64
+ function resolveDefaultSemanticPublicationReportOutFile(specId = '') {
65
+ const normalizedSpecId = normalizeString(specId);
66
+ if (!normalizedSpecId) {
67
+ return '';
68
+ }
69
+ return `.sce/specs/${normalizedSpecId}/publications/semantic/latest.json`;
70
+ }
71
+
72
+ function resolveSharedLibraryEntryRelativePath(targetLibrary = '', capabilityId = '') {
73
+ const normalizedLibrary = normalizeString(targetLibrary);
74
+ const normalizedCapabilityId = normalizeString(capabilityId);
75
+ if (!normalizedLibrary || !normalizedCapabilityId) {
76
+ return '';
77
+ }
78
+ return `${SHARED_LIBRARY_ROOT}/${normalizedLibrary}/entries/${normalizedCapabilityId}.json`;
79
+ }
80
+
81
+ function resolveSharedLibraryIndexRelativePath(targetLibrary = '') {
82
+ const normalizedLibrary = normalizeString(targetLibrary);
83
+ if (!normalizedLibrary) {
84
+ return '';
85
+ }
86
+ return `${SHARED_LIBRARY_ROOT}/${normalizedLibrary}/index.json`;
87
+ }
88
+
89
+ function buildPublicationReceiptId() {
90
+ return `sem-pub-${Date.now()}-${crypto.randomBytes(3).toString('hex')}`;
91
+ }
92
+
93
+ function assessGovernedPublicationGate(ledger = {}) {
94
+ const metrics = isObject(ledger.metrics) ? ledger.metrics : {};
95
+ const penaltyScore = Number(metrics.governance_penalty_score);
96
+ const refuseCount = Number.parseInt(`${metrics.governance_refuse_count || 0}`, 10) || 0;
97
+ const executionBlockedCount = Number.parseInt(`${metrics.governance_execution_blocked_count || 0}`, 10) || 0;
98
+ const governedCount = Number.parseInt(`${metrics.governance_governed_count || 0}`, 10) || 0;
99
+ const reasons = [];
100
+
101
+ if (refuseCount > 0) {
102
+ reasons.push({
103
+ reason: 'governance-refuse-history',
104
+ refuse_count: refuseCount
105
+ });
106
+ }
107
+ if (executionBlockedCount > 0) {
108
+ reasons.push({
109
+ reason: 'governance-execution-blocked-history',
110
+ execution_blocked_count: executionBlockedCount
111
+ });
112
+ }
113
+ if (Number.isFinite(penaltyScore) && penaltyScore >= GOVERNED_PUBLICATION_MAX_PENALTY_SCORE) {
114
+ reasons.push({
115
+ reason: 'governance-penalty-too-high',
116
+ governance_penalty_score: Number(penaltyScore.toFixed(2)),
117
+ max_allowed_penalty_score: GOVERNED_PUBLICATION_MAX_PENALTY_SCORE
118
+ });
119
+ }
120
+
121
+ return {
122
+ allowed: reasons.length === 0,
123
+ reasons,
124
+ metrics: {
125
+ governance_penalty_score: Number.isFinite(penaltyScore) ? Number(penaltyScore.toFixed(2)) : 0,
126
+ governance_governed_count: governedCount,
127
+ governance_refuse_count: refuseCount,
128
+ governance_execution_blocked_count: executionBlockedCount
129
+ }
130
+ };
131
+ }
132
+
133
+ async function applyGovernedPublicationBlockedState(lesson = {}, ledger = {}, context = {}, dependencies = {}) {
134
+ const store = dependencies.stateStore;
135
+ if (!store || !lesson || !lesson.lesson_id) {
136
+ return { lesson, ledger };
137
+ }
138
+
139
+ const generatedAt = normalizeString(context.generated_at) || new Date().toISOString();
140
+ const gate = isObject(context.governed_gate) ? context.governed_gate : {};
141
+ const blockedReason = gate.reasons && gate.reasons.length > 0
142
+ ? gate.reasons.map((item) => item.reason).join(',')
143
+ : 'governed-publication-gate';
144
+ const lessonMetadata = isObject(lesson.metadata) ? lesson.metadata : {};
145
+ const lessonPublish = isObject(lessonMetadata.publish) ? lessonMetadata.publish : {};
146
+ const blockedMetadata = {
147
+ ...lessonPublish,
148
+ consent_model: normalizeString(lessonPublish.consent_model) || 'adopt-implied',
149
+ publish_required: lessonPublish.publish_required === true,
150
+ publish_state: 'blocked',
151
+ blocked_at: generatedAt,
152
+ blocked_reason: blockedReason,
153
+ blocked_gate: gate
154
+ };
155
+
156
+ const blockedLesson = await store.upsertSemanticLessonCandidate({
157
+ ...lesson,
158
+ status: 'publish_blocked',
159
+ metadata: {
160
+ ...lessonMetadata,
161
+ publish: blockedMetadata
162
+ },
163
+ updated_at: generatedAt
164
+ });
165
+
166
+ const nextLedger = await store.upsertSemanticCapabilityLedger({
167
+ ...(ledger || {}),
168
+ capability_id: normalizeString(ledger && ledger.capability_id) || null,
169
+ lesson_id: normalizeString(lesson.lesson_id),
170
+ evaluation_run_id: normalizeString(ledger && ledger.evaluation_run_id) || null,
171
+ project_id: normalizeString(blockedLesson.project_id) || normalizeString(ledger && ledger.project_id) || null,
172
+ channel_id: normalizeString(ledger && ledger.channel_id) || normalizeString(lessonMetadata.channel_id) || null,
173
+ session_id: normalizeString(ledger && ledger.session_id) || normalizeString(lessonMetadata.session_id) || null,
174
+ scene_id: normalizeString(blockedLesson.scene_id) || normalizeString(ledger && ledger.scene_id) || null,
175
+ spec_id: normalizeString(blockedLesson.spec_id) || normalizeString(ledger && ledger.spec_id) || null,
176
+ target_library: normalizeString(ledger && ledger.target_library) || null,
177
+ capability_status: normalizeString(ledger && ledger.capability_status) || 'active',
178
+ publish_state: 'blocked',
179
+ publication: {
180
+ ...(isObject(ledger && ledger.publication) ? ledger.publication : {}),
181
+ ...blockedMetadata
182
+ },
183
+ lineage: isObject(ledger && ledger.lineage) ? ledger.lineage : {},
184
+ metrics: {
185
+ ...(isObject(ledger && ledger.metrics) ? ledger.metrics : {}),
186
+ governed_publication_gate_blocked: true
187
+ },
188
+ activation_count: ledger && ledger.activation_count,
189
+ activation_success_count: ledger && ledger.activation_success_count,
190
+ activation_mixed_count: ledger && ledger.activation_mixed_count,
191
+ activation_negative_count: ledger && ledger.activation_negative_count,
192
+ quarantine_count: ledger && ledger.quarantine_count,
193
+ last_activated_at: ledger && ledger.last_activated_at,
194
+ last_quality_signal: ledger && ledger.last_quality_signal,
195
+ last_runtime: ledger && ledger.last_runtime,
196
+ rank_score: ledger && ledger.rank_score,
197
+ created_at: normalizeString(ledger && ledger.created_at) || generatedAt,
198
+ updated_at: generatedAt
199
+ });
200
+
201
+ return {
202
+ lesson: blockedLesson,
203
+ ledger: nextLedger
204
+ };
205
+ }
206
+
207
+ async function upsertSharedLibraryIndex(targetLibrary, receipt = {}, dependencies = {}) {
208
+ const projectPath = dependencies.projectPath || process.cwd();
209
+ const fileSystem = dependencies.fileSystem || fs;
210
+ const libraryIndexRelativePath = resolveSharedLibraryIndexRelativePath(targetLibrary);
211
+ const libraryIndexAbsolutePath = path.join(projectPath, libraryIndexRelativePath);
212
+ const existingIndex = await readJsonIfExists(fileSystem, libraryIndexAbsolutePath, {
213
+ api_version: SHARED_LIBRARY_INDEX_API_VERSION,
214
+ target_library: targetLibrary,
215
+ updated_at: null,
216
+ entries: []
217
+ });
218
+ const existingEntries = Array.isArray(existingIndex.entries) ? existingIndex.entries : [];
219
+ const nextEntries = existingEntries
220
+ .filter((item) => normalizeString(item.capability_id) !== normalizeString(receipt.capability_id));
221
+ nextEntries.push({
222
+ capability_id: normalizeString(receipt.capability_id) || null,
223
+ lesson_id: normalizeString(receipt.lesson_id) || null,
224
+ project_id: normalizeString(receipt.project_id) || null,
225
+ channel_id: normalizeString(receipt.channel_id) || null,
226
+ session_id: normalizeString(receipt.session_id) || null,
227
+ scene_id: normalizeString(receipt.scene_id) || null,
228
+ spec_id: normalizeString(receipt.spec_id) || null,
229
+ published_at: normalizeString(receipt.published_at) || null,
230
+ entry_file: normalizeString(receipt.entry_file) || null,
231
+ source_manifest: normalizeString(receipt.source_manifest) || null,
232
+ receipt_id: normalizeString(receipt.receipt_id) || null
233
+ });
234
+ nextEntries.sort((left, right) => {
235
+ const leftTime = Date.parse(left.published_at || '') || 0;
236
+ const rightTime = Date.parse(right.published_at || '') || 0;
237
+ return rightTime - leftTime;
238
+ });
239
+ const nextIndex = {
240
+ api_version: SHARED_LIBRARY_INDEX_API_VERSION,
241
+ target_library: targetLibrary,
242
+ updated_at: normalizeString(receipt.published_at) || new Date().toISOString(),
243
+ entries: nextEntries
244
+ };
245
+ await fileSystem.ensureDir(path.dirname(libraryIndexAbsolutePath));
246
+ await fileSystem.writeJson(libraryIndexAbsolutePath, nextIndex, { spaces: 2 });
247
+
248
+ const rootIndexAbsolutePath = path.join(projectPath, SHARED_LIBRARY_ROOT, 'index.json');
249
+ const existingRootIndex = await readJsonIfExists(fileSystem, rootIndexAbsolutePath, {
250
+ api_version: SHARED_LIBRARY_ROOT_INDEX_API_VERSION,
251
+ updated_at: null,
252
+ libraries: []
253
+ });
254
+ const existingLibraries = Array.isArray(existingRootIndex.libraries) ? existingRootIndex.libraries : [];
255
+ const nextLibraries = existingLibraries
256
+ .filter((item) => normalizeString(item.target_library) !== targetLibrary);
257
+ nextLibraries.push({
258
+ target_library: targetLibrary,
259
+ entry_count: nextEntries.length,
260
+ index_file: libraryIndexRelativePath,
261
+ updated_at: nextIndex.updated_at
262
+ });
263
+ nextLibraries.sort((left, right) => `${left.target_library}`.localeCompare(`${right.target_library}`));
264
+ await fileSystem.ensureDir(path.dirname(rootIndexAbsolutePath));
265
+ await fileSystem.writeJson(rootIndexAbsolutePath, {
266
+ api_version: SHARED_LIBRARY_ROOT_INDEX_API_VERSION,
267
+ updated_at: nextIndex.updated_at,
268
+ libraries: nextLibraries
269
+ }, { spaces: 2 });
270
+
271
+ return {
272
+ library_index_file: libraryIndexRelativePath,
273
+ root_index_file: `${SHARED_LIBRARY_ROOT}/index.json`
274
+ };
275
+ }
276
+
277
+ async function publishSemanticLibraryDelta(options = {}, dependencies = {}) {
278
+ const projectPath = dependencies.projectPath || process.cwd();
279
+ const fileSystem = dependencies.fileSystem || fs;
280
+ const store = dependencies.stateStore || getSceStateStore(projectPath, {
281
+ fileSystem,
282
+ env: dependencies.env,
283
+ sqliteModule: dependencies.sqliteModule,
284
+ noCache: dependencies.noCache === true
285
+ });
286
+
287
+ const requestedProjectId = normalizeString(options.project_id || options.projectId);
288
+ const requestedSpecId = normalizeString(options.spec_id || options.specId);
289
+ const requestedLessonId = normalizeString(options.lesson_id || options.lessonId);
290
+ const requestedCapabilityId = normalizeString(options.capability_id || options.capabilityId);
291
+ const inputFile = normalizeString(options.input_file || options.inputFile);
292
+ const limit = normalizeInteger(options.limit, 100);
293
+ const syncLedger = typeof dependencies.syncSemanticCapabilityLedger === 'function'
294
+ ? dependencies.syncSemanticCapabilityLedger
295
+ : null;
296
+
297
+ let deltaPayload;
298
+ let sourceManifest = '';
299
+ if (inputFile) {
300
+ const absoluteInputPath = path.isAbsolute(inputFile)
301
+ ? inputFile
302
+ : path.join(projectPath, inputFile);
303
+ deltaPayload = await fileSystem.readJson(absoluteInputPath);
304
+ sourceManifest = toProjectRelative(projectPath, absoluteInputPath);
305
+ } else {
306
+ deltaPayload = await exportSemanticLibraryDelta({
307
+ project_id: requestedProjectId,
308
+ spec_id: requestedSpecId,
309
+ lesson_id: requestedLessonId,
310
+ capability_id: requestedCapabilityId,
311
+ limit
312
+ }, {
313
+ ...dependencies,
314
+ projectPath,
315
+ fileSystem,
316
+ stateStore: store
317
+ });
318
+ sourceManifest = normalizeString(deltaPayload && deltaPayload.out_file);
319
+ }
320
+
321
+ const entries = Array.isArray(deltaPayload && deltaPayload.entries) ? deltaPayload.entries : [];
322
+ const filteredEntries = entries.filter((entry) => {
323
+ if (requestedLessonId && normalizeString(entry && entry.lesson_id) !== requestedLessonId) {
324
+ return false;
325
+ }
326
+ if (requestedCapabilityId && normalizeString(entry && entry.capability_id) !== requestedCapabilityId) {
327
+ return false;
328
+ }
329
+ return true;
330
+ });
331
+ const projectId = requestedProjectId || normalizeString(deltaPayload && deltaPayload.project_id) || null;
332
+ const specId = requestedSpecId || normalizeString(deltaPayload && deltaPayload.spec_id) || null;
333
+ const generatedAt = new Date().toISOString();
334
+ const published = [];
335
+ const blocked = [];
336
+
337
+ for (const entry of filteredEntries) {
338
+ const capabilityId = normalizeString(entry && entry.capability_id);
339
+ const lessonId = normalizeString(entry && entry.lesson_id);
340
+ const targetLibrary = normalizeString(entry && entry.target_library);
341
+ const sanitized = Boolean(entry && entry.sanitized === true);
342
+
343
+ if (!capabilityId || !lessonId) {
344
+ blocked.push({
345
+ capability_id: capabilityId || null,
346
+ lesson_id: lessonId || null,
347
+ target_library: targetLibrary || null,
348
+ reason: 'missing-capability-or-lesson-id'
349
+ });
350
+ continue;
351
+ }
352
+ if (!SUPPORTED_TARGET_LIBRARIES.has(targetLibrary)) {
353
+ blocked.push({
354
+ capability_id: capabilityId,
355
+ lesson_id: lessonId,
356
+ target_library: targetLibrary || null,
357
+ reason: 'unsupported-target-library'
358
+ });
359
+ continue;
360
+ }
361
+ if (!sanitized) {
362
+ blocked.push({
363
+ capability_id: capabilityId,
364
+ lesson_id: lessonId,
365
+ target_library: targetLibrary,
366
+ reason: 'unsanitized-delta'
367
+ });
368
+ continue;
369
+ }
370
+
371
+ const lesson = await store.getSemanticLessonCandidate(lessonId);
372
+ if (!lesson) {
373
+ blocked.push({
374
+ capability_id: capabilityId,
375
+ lesson_id: lessonId,
376
+ target_library: targetLibrary,
377
+ reason: 'lesson-not-found'
378
+ });
379
+ continue;
380
+ }
381
+ const existingLedger = await store.getSemanticCapabilityLedger(capabilityId);
382
+ const governedGate = assessGovernedPublicationGate(existingLedger || {});
383
+ if (!governedGate.allowed) {
384
+ await applyGovernedPublicationBlockedState(lesson, existingLedger || {}, {
385
+ generated_at: generatedAt,
386
+ governed_gate: governedGate
387
+ }, {
388
+ stateStore: store
389
+ });
390
+ blocked.push({
391
+ capability_id: capabilityId,
392
+ lesson_id: lessonId,
393
+ target_library: targetLibrary,
394
+ reason: 'governed-publication-gate',
395
+ gate: governedGate
396
+ });
397
+ continue;
398
+ }
399
+
400
+ const entryRelativePath = resolveSharedLibraryEntryRelativePath(targetLibrary, capabilityId);
401
+ const entryAbsolutePath = path.join(projectPath, entryRelativePath);
402
+ const receiptId = buildPublicationReceiptId();
403
+ const lessonMetadata = isObject(lesson.metadata) ? lesson.metadata : {};
404
+ const archive = isObject(entry.archive)
405
+ ? entry.archive
406
+ : (isObject(lessonMetadata.archive) ? lessonMetadata.archive : null);
407
+ const libraryEntry = {
408
+ api_version: SHARED_LIBRARY_ENTRY_API_VERSION,
409
+ receipt_id: receiptId,
410
+ published_at: generatedAt,
411
+ project_id: normalizeString(lesson.project_id) || projectId,
412
+ channel_id: normalizeString(lessonMetadata.channel_id) || null,
413
+ session_id: normalizeString(lessonMetadata.session_id) || null,
414
+ scene_id: normalizeString(lesson.scene_id) || normalizeString(entry.scene_id) || null,
415
+ spec_id: normalizeString(lesson.spec_id) || specId,
416
+ capability_id: capabilityId,
417
+ lesson_id: lessonId,
418
+ target_library: targetLibrary,
419
+ title: normalizeString(entry.title) || normalizeString(lesson.title) || null,
420
+ summary: normalizeString(entry.summary) || normalizeString(lesson.summary) || null,
421
+ heuristic: normalizeString(entry.heuristic) || normalizeString(lesson.heuristic) || null,
422
+ lesson_type: normalizeString(entry.lesson_type) || normalizeString(lesson.lesson_type) || null,
423
+ confidence: Number.isFinite(Number(entry.confidence)) ? Number(entry.confidence) : Number(lesson.confidence),
424
+ lineage: isObject(entry.lineage) ? entry.lineage : {},
425
+ archive,
426
+ publication: {
427
+ ...(isObject(entry.publication) ? entry.publication : {}),
428
+ publish_state: 'published-shared',
429
+ source_manifest: sourceManifest || null,
430
+ entry_file: entryRelativePath
431
+ },
432
+ sanitized: true
433
+ };
434
+
435
+ await fileSystem.ensureDir(path.dirname(entryAbsolutePath));
436
+ await fileSystem.writeJson(entryAbsolutePath, libraryEntry, { spaces: 2 });
437
+
438
+ const indexPointers = await upsertSharedLibraryIndex(targetLibrary, {
439
+ capability_id: capabilityId,
440
+ lesson_id: lessonId,
441
+ project_id: libraryEntry.project_id,
442
+ channel_id: libraryEntry.channel_id,
443
+ session_id: libraryEntry.session_id,
444
+ scene_id: libraryEntry.scene_id,
445
+ spec_id: libraryEntry.spec_id,
446
+ published_at: generatedAt,
447
+ entry_file: entryRelativePath,
448
+ source_manifest: sourceManifest,
449
+ receipt_id: receiptId
450
+ }, {
451
+ projectPath,
452
+ fileSystem
453
+ });
454
+
455
+ const lessonPublish = isObject(lesson.metadata) && isObject(lesson.metadata.publish)
456
+ ? lesson.metadata.publish
457
+ : {};
458
+ const publishedMetadata = {
459
+ ...lessonPublish,
460
+ consent_model: normalizeString(lessonPublish.consent_model) || 'adopt-implied',
461
+ publish_required: lessonPublish.publish_required === true,
462
+ publish_state: 'published-shared',
463
+ published_at: generatedAt,
464
+ published_library: targetLibrary,
465
+ published_receipt_id: receiptId,
466
+ published_entry_file: entryRelativePath,
467
+ published_index_file: indexPointers.library_index_file,
468
+ source_manifest: sourceManifest || null
469
+ };
470
+ const publishedLesson = await store.upsertSemanticLessonCandidate({
471
+ ...lesson,
472
+ status: 'published-shared',
473
+ metadata: {
474
+ ...(isObject(lesson.metadata) ? lesson.metadata : {}),
475
+ ...(archive ? { archive } : {}),
476
+ publish: publishedMetadata
477
+ },
478
+ updated_at: generatedAt
479
+ });
480
+
481
+ await store.upsertSemanticCapabilityLedger({
482
+ ...(existingLedger || {}),
483
+ capability_id: capabilityId,
484
+ lesson_id: lessonId,
485
+ evaluation_run_id: normalizeString(entry.lineage && entry.lineage.evaluation_run_id)
486
+ || normalizeString(existingLedger && existingLedger.evaluation_run_id)
487
+ || null,
488
+ project_id: normalizeString(publishedLesson.project_id) || normalizeString(existingLedger && existingLedger.project_id) || projectId,
489
+ channel_id: normalizeString(existingLedger && existingLedger.channel_id) || null,
490
+ session_id: normalizeString(existingLedger && existingLedger.session_id) || null,
491
+ scene_id: normalizeString(publishedLesson.scene_id) || normalizeString(existingLedger && existingLedger.scene_id) || null,
492
+ spec_id: normalizeString(publishedLesson.spec_id) || normalizeString(existingLedger && existingLedger.spec_id) || specId,
493
+ target_library: targetLibrary,
494
+ capability_status: normalizeString(existingLedger && existingLedger.capability_status) || 'active',
495
+ publish_state: 'published-shared',
496
+ publication: {
497
+ ...(isObject(existingLedger && existingLedger.publication) ? existingLedger.publication : {}),
498
+ ...publishedMetadata,
499
+ target_library: targetLibrary,
500
+ ...(archive ? { archive } : {})
501
+ },
502
+ lineage: {
503
+ ...(isObject(existingLedger && existingLedger.lineage) ? existingLedger.lineage : {}),
504
+ ...(archive ? { archive } : {})
505
+ },
506
+ metrics: {
507
+ ...(isObject(existingLedger && existingLedger.metrics) ? existingLedger.metrics : {}),
508
+ ...summarizeArchiveMetrics(archive)
509
+ },
510
+ activation_count: existingLedger && existingLedger.activation_count,
511
+ activation_success_count: existingLedger && existingLedger.activation_success_count,
512
+ activation_mixed_count: existingLedger && existingLedger.activation_mixed_count,
513
+ activation_negative_count: existingLedger && existingLedger.activation_negative_count,
514
+ quarantine_count: existingLedger && existingLedger.quarantine_count,
515
+ last_activated_at: existingLedger && existingLedger.last_activated_at,
516
+ last_quality_signal: existingLedger && existingLedger.last_quality_signal,
517
+ last_runtime: existingLedger && existingLedger.last_runtime,
518
+ rank_score: existingLedger && existingLedger.rank_score,
519
+ created_at: normalizeString(existingLedger && existingLedger.created_at) || generatedAt,
520
+ updated_at: generatedAt
521
+ });
522
+
523
+ if (syncLedger) {
524
+ await syncLedger(publishedLesson, {
525
+ capability_id: capabilityId,
526
+ evaluation_run_id: normalizeString(entry.lineage && entry.lineage.evaluation_run_id)
527
+ || normalizeString(existingLedger && existingLedger.evaluation_run_id)
528
+ || null,
529
+ target_library: targetLibrary,
530
+ capability_status: normalizeString(existingLedger && existingLedger.capability_status) || 'active',
531
+ publish_state: 'published-shared',
532
+ updated_at: generatedAt
533
+ }, {
534
+ ...dependencies,
535
+ projectPath,
536
+ fileSystem,
537
+ stateStore: store
538
+ });
539
+ }
540
+
541
+ published.push({
542
+ capability_id: capabilityId,
543
+ lesson_id: lessonId,
544
+ target_library: targetLibrary,
545
+ published_at: generatedAt,
546
+ receipt_id: receiptId,
547
+ entry_file: entryRelativePath,
548
+ index_file: indexPointers.library_index_file
549
+ });
550
+ }
551
+
552
+ const payload = {
553
+ api_version: SEMANTIC_PUBLICATION_REPORT_API_VERSION,
554
+ mode: 'semantic-publish-delta',
555
+ success: blocked.length === 0,
556
+ generated_at: generatedAt,
557
+ project_id: projectId,
558
+ spec_id: specId,
559
+ source_manifest: sourceManifest || null,
560
+ mirror_root: SHARED_LIBRARY_ROOT,
561
+ totals: {
562
+ entries: entries.length,
563
+ matched: filteredEntries.length,
564
+ published: published.length,
565
+ blocked: blocked.length
566
+ },
567
+ published,
568
+ blocked
569
+ };
570
+
571
+ const outFile = normalizeString(options.out_file || options.outFile)
572
+ || resolveDefaultSemanticPublicationReportOutFile(specId);
573
+ if (outFile) {
574
+ const absoluteOutPath = path.isAbsolute(outFile)
575
+ ? outFile
576
+ : path.join(projectPath, outFile);
577
+ await fileSystem.ensureDir(path.dirname(absoluteOutPath));
578
+ await fileSystem.writeJson(absoluteOutPath, payload, { spaces: 2 });
579
+ payload.out_file = toProjectRelative(projectPath, absoluteOutPath);
580
+ }
581
+
582
+ return payload;
583
+ }
584
+
585
+ module.exports = {
586
+ SHARED_LIBRARY_ROOT,
587
+ resolveDefaultSemanticPublicationReportOutFile,
588
+ resolveSharedLibraryEntryRelativePath,
589
+ resolveSharedLibraryIndexRelativePath,
590
+ upsertSharedLibraryIndex,
591
+ publishSemanticLibraryDelta
592
+ };