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.
- package/CHANGELOG.md +16 -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 +382 -6
- package/docs/document-governance.md +3 -2
- package/docs/integration-modes.md +62 -478
- package/docs/integration-philosophy.md +56 -263
- package/docs/magicball-project-portfolio-contract.md +114 -2
- 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 +2 -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 +2 -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 +95 -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 +60 -8
- 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-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,283 @@
|
|
|
1
|
+
const { getSceStateStore } = require('../state/sce-state-store');
|
|
2
|
+
const { buildSemanticArchiveDecision } = require('./archive-routing');
|
|
3
|
+
|
|
4
|
+
function normalizeString(value) {
|
|
5
|
+
if (typeof value !== 'string') {
|
|
6
|
+
return '';
|
|
7
|
+
}
|
|
8
|
+
return value.trim();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function normalizeInteger(value, fallback = 0) {
|
|
12
|
+
const parsed = Number.parseInt(`${value}`, 10);
|
|
13
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
14
|
+
return fallback;
|
|
15
|
+
}
|
|
16
|
+
return parsed;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isObject(value) {
|
|
20
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readLessonPublishState(lesson = {}) {
|
|
24
|
+
const metadata = isObject(lesson.metadata) ? lesson.metadata : {};
|
|
25
|
+
const publish = isObject(metadata.publish) ? metadata.publish : {};
|
|
26
|
+
return normalizeString(publish.publish_state) || normalizeString(lesson.status) || 'none';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function readArchiveDecision(lesson = {}, ledger = {}) {
|
|
30
|
+
const lessonMetadata = isObject(lesson.metadata) ? lesson.metadata : {};
|
|
31
|
+
const lessonArchive = isObject(lessonMetadata.archive) ? lessonMetadata.archive : null;
|
|
32
|
+
if (lessonArchive && normalizeString(lessonArchive.target_library)) {
|
|
33
|
+
return lessonArchive;
|
|
34
|
+
}
|
|
35
|
+
const ledgerLineage = isObject(ledger.lineage) ? ledger.lineage : {};
|
|
36
|
+
const lineageArchive = isObject(ledgerLineage.archive) ? ledgerLineage.archive : null;
|
|
37
|
+
if (lineageArchive && normalizeString(lineageArchive.target_library)) {
|
|
38
|
+
return lineageArchive;
|
|
39
|
+
}
|
|
40
|
+
const ledgerPublication = isObject(ledger.publication) ? ledger.publication : {};
|
|
41
|
+
const publicationArchive = isObject(ledgerPublication.archive) ? ledgerPublication.archive : null;
|
|
42
|
+
if (publicationArchive && normalizeString(publicationArchive.target_library)) {
|
|
43
|
+
return publicationArchive;
|
|
44
|
+
}
|
|
45
|
+
return buildSemanticArchiveDecision({
|
|
46
|
+
project_id: lesson.project_id || ledger.project_id,
|
|
47
|
+
scene_id: lesson.scene_id || ledger.scene_id,
|
|
48
|
+
spec_id: lesson.spec_id || ledger.spec_id,
|
|
49
|
+
lesson_type: lesson.lesson_type || null,
|
|
50
|
+
metadata: {
|
|
51
|
+
channel_id: normalizeString(lessonMetadata.channel_id || ledger.channel_id) || null,
|
|
52
|
+
session_id: normalizeString(lessonMetadata.session_id || ledger.session_id) || null
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function createBucket(targetLibrary, archiveDecision = {}) {
|
|
58
|
+
return {
|
|
59
|
+
target_library: targetLibrary,
|
|
60
|
+
archive_class: normalizeString(archiveDecision.archive_class) || null,
|
|
61
|
+
repository_name: normalizeString(archiveDecision.repository_name) || targetLibrary || null,
|
|
62
|
+
reuse_scope: normalizeString(archiveDecision.reuse_scope) || null,
|
|
63
|
+
lesson_count: 0,
|
|
64
|
+
ledger_count: 0,
|
|
65
|
+
candidate_count: 0,
|
|
66
|
+
publish_pending_count: 0,
|
|
67
|
+
published_shared_count: 0,
|
|
68
|
+
publish_blocked_count: 0,
|
|
69
|
+
deprecated_count: 0,
|
|
70
|
+
active_capability_count: 0,
|
|
71
|
+
quarantined_capability_count: 0,
|
|
72
|
+
deprecated_capability_count: 0
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function incrementCounter(map, key) {
|
|
77
|
+
const normalizedKey = normalizeString(key) || 'unknown';
|
|
78
|
+
map[normalizedKey] = (map[normalizedKey] || 0) + 1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function sortCounters(map = {}, keyName = 'key') {
|
|
82
|
+
return Object.entries(map)
|
|
83
|
+
.map(([key, count]) => ({ [keyName]: key, count }))
|
|
84
|
+
.sort((left, right) => {
|
|
85
|
+
if (right.count !== left.count) {
|
|
86
|
+
return right.count - left.count;
|
|
87
|
+
}
|
|
88
|
+
return `${left[keyName]}`.localeCompare(`${right[keyName]}`);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function buildSemanticArchiveSummary(input = {}) {
|
|
93
|
+
const lessons = Array.isArray(input.lessons) ? input.lessons : [];
|
|
94
|
+
const capabilityLedgers = Array.isArray(input.capabilityLedgers) ? input.capabilityLedgers : [];
|
|
95
|
+
const recentLimit = normalizeInteger(input.recent_limit || input.recentLimit, 10);
|
|
96
|
+
const ledgersByLessonId = {};
|
|
97
|
+
for (const ledger of capabilityLedgers) {
|
|
98
|
+
const lessonId = normalizeString(ledger.lesson_id);
|
|
99
|
+
if (lessonId && !ledgersByLessonId[lessonId]) {
|
|
100
|
+
ledgersByLessonId[lessonId] = ledger;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const byLibrary = {};
|
|
105
|
+
const byArchiveClass = {};
|
|
106
|
+
const byReuseScope = {};
|
|
107
|
+
let missingArchiveCount = 0;
|
|
108
|
+
const recentItems = [];
|
|
109
|
+
const seenLessonIds = new Set();
|
|
110
|
+
|
|
111
|
+
for (const lesson of lessons) {
|
|
112
|
+
const lessonId = normalizeString(lesson.lesson_id);
|
|
113
|
+
const ledger = ledgersByLessonId[lessonId] || {};
|
|
114
|
+
const archive = readArchiveDecision(lesson, ledger);
|
|
115
|
+
const targetLibrary = normalizeString(archive.target_library);
|
|
116
|
+
if (!targetLibrary) {
|
|
117
|
+
missingArchiveCount += 1;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (!byLibrary[targetLibrary]) {
|
|
121
|
+
byLibrary[targetLibrary] = createBucket(targetLibrary, archive);
|
|
122
|
+
}
|
|
123
|
+
const bucket = byLibrary[targetLibrary];
|
|
124
|
+
bucket.lesson_count += 1;
|
|
125
|
+
const lessonStatus = normalizeString(lesson.status);
|
|
126
|
+
const publishState = readLessonPublishState(lesson);
|
|
127
|
+
if (lessonStatus === 'candidate') {
|
|
128
|
+
bucket.candidate_count += 1;
|
|
129
|
+
}
|
|
130
|
+
if (publishState === 'pending' || lessonStatus === 'publish_pending') {
|
|
131
|
+
bucket.publish_pending_count += 1;
|
|
132
|
+
}
|
|
133
|
+
if (publishState === 'published-shared' || lessonStatus === 'published-shared') {
|
|
134
|
+
bucket.published_shared_count += 1;
|
|
135
|
+
}
|
|
136
|
+
if (publishState === 'blocked' || lessonStatus === 'publish_blocked') {
|
|
137
|
+
bucket.publish_blocked_count += 1;
|
|
138
|
+
}
|
|
139
|
+
if (lessonStatus === 'deprecated') {
|
|
140
|
+
bucket.deprecated_count += 1;
|
|
141
|
+
}
|
|
142
|
+
incrementCounter(byArchiveClass, archive.archive_class);
|
|
143
|
+
incrementCounter(byReuseScope, archive.reuse_scope);
|
|
144
|
+
recentItems.push({
|
|
145
|
+
lesson_id: lessonId || null,
|
|
146
|
+
capability_id: normalizeString(ledger.capability_id) || null,
|
|
147
|
+
project_id: normalizeString(lesson.project_id) || normalizeString(ledger.project_id) || null,
|
|
148
|
+
channel_id: normalizeString((lesson.metadata && lesson.metadata.channel_id) || ledger.channel_id) || null,
|
|
149
|
+
session_id: normalizeString((lesson.metadata && lesson.metadata.session_id) || ledger.session_id) || null,
|
|
150
|
+
scene_id: normalizeString(lesson.scene_id) || normalizeString(ledger.scene_id) || null,
|
|
151
|
+
spec_id: normalizeString(lesson.spec_id) || normalizeString(ledger.spec_id) || null,
|
|
152
|
+
target_library: targetLibrary,
|
|
153
|
+
archive_class: normalizeString(archive.archive_class) || null,
|
|
154
|
+
reuse_scope: normalizeString(archive.reuse_scope) || null,
|
|
155
|
+
reason_code: normalizeString(archive.reason_code) || null,
|
|
156
|
+
lesson_status: lessonStatus || null,
|
|
157
|
+
publish_state: publishState || null,
|
|
158
|
+
updated_at: normalizeString(lesson.updated_at) || normalizeString(ledger.updated_at) || null
|
|
159
|
+
});
|
|
160
|
+
if (lessonId) {
|
|
161
|
+
seenLessonIds.add(lessonId);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for (const ledger of capabilityLedgers) {
|
|
166
|
+
const archive = readArchiveDecision({}, ledger);
|
|
167
|
+
const targetLibrary = normalizeString(ledger.target_library) || normalizeString(archive.target_library);
|
|
168
|
+
if (!targetLibrary) {
|
|
169
|
+
missingArchiveCount += 1;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (!byLibrary[targetLibrary]) {
|
|
173
|
+
byLibrary[targetLibrary] = createBucket(targetLibrary, archive);
|
|
174
|
+
}
|
|
175
|
+
const bucket = byLibrary[targetLibrary];
|
|
176
|
+
bucket.ledger_count += 1;
|
|
177
|
+
const capabilityStatus = normalizeString(ledger.capability_status);
|
|
178
|
+
if (capabilityStatus === 'active') {
|
|
179
|
+
bucket.active_capability_count += 1;
|
|
180
|
+
}
|
|
181
|
+
if (capabilityStatus === 'quarantined') {
|
|
182
|
+
bucket.quarantined_capability_count += 1;
|
|
183
|
+
}
|
|
184
|
+
if (capabilityStatus === 'deprecated') {
|
|
185
|
+
bucket.deprecated_capability_count += 1;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const lessonId = normalizeString(ledger.lesson_id);
|
|
189
|
+
if (lessonId && !seenLessonIds.has(lessonId)) {
|
|
190
|
+
recentItems.push({
|
|
191
|
+
lesson_id: lessonId,
|
|
192
|
+
capability_id: normalizeString(ledger.capability_id) || null,
|
|
193
|
+
project_id: normalizeString(ledger.project_id) || null,
|
|
194
|
+
channel_id: normalizeString(ledger.channel_id) || null,
|
|
195
|
+
session_id: normalizeString(ledger.session_id) || null,
|
|
196
|
+
scene_id: normalizeString(ledger.scene_id) || null,
|
|
197
|
+
spec_id: normalizeString(ledger.spec_id) || null,
|
|
198
|
+
target_library: targetLibrary,
|
|
199
|
+
archive_class: normalizeString(archive.archive_class) || null,
|
|
200
|
+
reuse_scope: normalizeString(archive.reuse_scope) || null,
|
|
201
|
+
reason_code: normalizeString(archive.reason_code) || null,
|
|
202
|
+
lesson_status: null,
|
|
203
|
+
publish_state: normalizeString(ledger.publish_state) || null,
|
|
204
|
+
updated_at: normalizeString(ledger.updated_at) || null
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
totals: {
|
|
211
|
+
lesson_total: lessons.length,
|
|
212
|
+
ledger_total: capabilityLedgers.length,
|
|
213
|
+
missing_archive_count: missingArchiveCount,
|
|
214
|
+
library_count: Object.keys(byLibrary).length
|
|
215
|
+
},
|
|
216
|
+
by_library: Object.values(byLibrary).sort((left, right) => `${left.target_library}`.localeCompare(`${right.target_library}`)),
|
|
217
|
+
by_archive_class: sortCounters(byArchiveClass, 'archive_class'),
|
|
218
|
+
by_reuse_scope: sortCounters(byReuseScope, 'reuse_scope'),
|
|
219
|
+
recent_items: recentItems
|
|
220
|
+
.sort((left, right) => `${right.updated_at || ''}`.localeCompare(`${left.updated_at || ''}`))
|
|
221
|
+
.slice(0, recentLimit)
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function buildSemanticArchiveReport(options = {}, dependencies = {}) {
|
|
226
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
227
|
+
const store = dependencies.stateStore || getSceStateStore(projectPath, {
|
|
228
|
+
fileSystem: dependencies.fileSystem,
|
|
229
|
+
env: dependencies.env,
|
|
230
|
+
sqliteModule: dependencies.sqliteModule,
|
|
231
|
+
noCache: dependencies.noCache === true
|
|
232
|
+
});
|
|
233
|
+
const projectId = normalizeString(options.project_id || options.projectId);
|
|
234
|
+
const specId = normalizeString(options.spec_id || options.specId);
|
|
235
|
+
const sessionId = normalizeString(options.session_id || options.sessionId);
|
|
236
|
+
const limit = normalizeInteger(options.limit, 200);
|
|
237
|
+
const recentLimit = normalizeInteger(options.recent_limit || options.recentLimit, 10);
|
|
238
|
+
|
|
239
|
+
let lessons = await store.listSemanticLessonCandidates({
|
|
240
|
+
project_id: projectId,
|
|
241
|
+
limit
|
|
242
|
+
});
|
|
243
|
+
lessons = Array.isArray(lessons) ? lessons : [];
|
|
244
|
+
if (specId) {
|
|
245
|
+
lessons = lessons.filter((item) => normalizeString(item.spec_id) === specId);
|
|
246
|
+
}
|
|
247
|
+
if (sessionId) {
|
|
248
|
+
lessons = lessons.filter((item) => normalizeString(item.metadata && item.metadata.session_id) === sessionId);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let capabilityLedgers = await store.listSemanticCapabilityLedgers({
|
|
252
|
+
project_id: projectId,
|
|
253
|
+
spec_id: specId,
|
|
254
|
+
limit
|
|
255
|
+
});
|
|
256
|
+
capabilityLedgers = Array.isArray(capabilityLedgers) ? capabilityLedgers : [];
|
|
257
|
+
if (sessionId) {
|
|
258
|
+
capabilityLedgers = capabilityLedgers.filter((item) => normalizeString(item.session_id) === sessionId);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
mode: 'semantic-archive-report',
|
|
263
|
+
success: true,
|
|
264
|
+
generated_at: new Date().toISOString(),
|
|
265
|
+
filters: {
|
|
266
|
+
project_id: projectId || null,
|
|
267
|
+
spec_id: specId || null,
|
|
268
|
+
session_id: sessionId || null,
|
|
269
|
+
limit,
|
|
270
|
+
recent_limit: recentLimit
|
|
271
|
+
},
|
|
272
|
+
...buildSemanticArchiveSummary({
|
|
273
|
+
lessons,
|
|
274
|
+
capabilityLedgers,
|
|
275
|
+
recent_limit: recentLimit
|
|
276
|
+
})
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
module.exports = {
|
|
281
|
+
buildSemanticArchiveSummary,
|
|
282
|
+
buildSemanticArchiveReport
|
|
283
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
function normalizeString(value) {
|
|
2
|
+
if (typeof value !== 'string') {
|
|
3
|
+
return '';
|
|
4
|
+
}
|
|
5
|
+
return value.trim();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function normalizeMetadata(payload = {}) {
|
|
9
|
+
return payload && typeof payload === 'object' ? payload : {};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function buildSemanticArchiveDecision(lesson = {}) {
|
|
13
|
+
const metadata = normalizeMetadata(lesson.metadata);
|
|
14
|
+
const lessonType = normalizeString(lesson.lesson_type);
|
|
15
|
+
const sceneId = normalizeString(lesson.scene_id) || null;
|
|
16
|
+
const specId = normalizeString(lesson.spec_id) || null;
|
|
17
|
+
const projectId = normalizeString(lesson.project_id) || null;
|
|
18
|
+
const channelId = normalizeString(metadata.channel_id || metadata.channelId) || null;
|
|
19
|
+
const sessionId = normalizeString(metadata.session_id || metadata.sessionId) || null;
|
|
20
|
+
|
|
21
|
+
if (lessonType === 'failure-pattern') {
|
|
22
|
+
return {
|
|
23
|
+
target_library: 'experience-library',
|
|
24
|
+
archive_class: 'experience',
|
|
25
|
+
repository_name: 'experience-library',
|
|
26
|
+
reuse_scope: 'cross-project',
|
|
27
|
+
reason_code: 'failure-pattern-routes-to-experience-library',
|
|
28
|
+
project_id: projectId,
|
|
29
|
+
channel_id: channelId,
|
|
30
|
+
session_id: sessionId,
|
|
31
|
+
scene_id: sceneId,
|
|
32
|
+
spec_id: specId
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (sceneId === 'scene.sce-semantic-kernel') {
|
|
37
|
+
return {
|
|
38
|
+
target_library: 'ontology-capability-library',
|
|
39
|
+
archive_class: 'ontology-capability',
|
|
40
|
+
repository_name: 'ontology-capability-library',
|
|
41
|
+
reuse_scope: 'cross-project',
|
|
42
|
+
reason_code: 'semantic-kernel-scene-routes-to-ontology-capability-library',
|
|
43
|
+
project_id: projectId,
|
|
44
|
+
channel_id: channelId,
|
|
45
|
+
session_id: sessionId,
|
|
46
|
+
scene_id: sceneId,
|
|
47
|
+
spec_id: specId
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
target_library: 'application-library',
|
|
53
|
+
archive_class: 'application',
|
|
54
|
+
repository_name: 'application-library',
|
|
55
|
+
reuse_scope: 'project-or-spec',
|
|
56
|
+
reason_code: 'project-scoped-pattern-routes-to-application-library',
|
|
57
|
+
project_id: projectId,
|
|
58
|
+
channel_id: channelId,
|
|
59
|
+
session_id: sessionId,
|
|
60
|
+
scene_id: sceneId,
|
|
61
|
+
spec_id: specId
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
buildSemanticArchiveDecision
|
|
67
|
+
};
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const { getSceStateStore } = require('../state/sce-state-store');
|
|
4
|
+
|
|
5
|
+
function normalizeString(value) {
|
|
6
|
+
if (typeof value !== 'string') {
|
|
7
|
+
return '';
|
|
8
|
+
}
|
|
9
|
+
return value.trim();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function normalizeInteger(value, fallback = 0) {
|
|
13
|
+
const parsed = Number.parseInt(`${value}`, 10);
|
|
14
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
15
|
+
return fallback;
|
|
16
|
+
}
|
|
17
|
+
return parsed;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function readLessonPublish(lesson = {}) {
|
|
21
|
+
if (lesson.metadata && typeof lesson.metadata === 'object'
|
|
22
|
+
&& lesson.metadata.publish
|
|
23
|
+
&& typeof lesson.metadata.publish === 'object') {
|
|
24
|
+
return lesson.metadata.publish;
|
|
25
|
+
}
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function readJsonIfExists(filePath, fileSystem = fs) {
|
|
30
|
+
if (!await fileSystem.pathExists(filePath)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
return await fileSystem.readJson(filePath);
|
|
35
|
+
} catch (_error) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function incrementCounter(map, key) {
|
|
41
|
+
const normalizedKey = normalizeString(key) || 'unknown';
|
|
42
|
+
map[normalizedKey] = (map[normalizedKey] || 0) + 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function sortCounters(map = {}, keyName = 'reason') {
|
|
46
|
+
return Object.entries(map)
|
|
47
|
+
.map(([key, count]) => ({ [keyName]: key, count }))
|
|
48
|
+
.sort((left, right) => {
|
|
49
|
+
if (right.count !== left.count) {
|
|
50
|
+
return right.count - left.count;
|
|
51
|
+
}
|
|
52
|
+
return `${left[keyName]}`.localeCompare(`${right[keyName]}`);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function listUniqueSpecIds(lessons = [], explicitSpecId = '') {
|
|
57
|
+
const requested = normalizeString(explicitSpecId);
|
|
58
|
+
if (requested) {
|
|
59
|
+
return [requested];
|
|
60
|
+
}
|
|
61
|
+
return [...new Set(lessons.map((item) => normalizeString(item.spec_id)).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function collectBlockedSpecIds(localBlockedLessons = [], mergeReceipts = [], releaseReceipts = []) {
|
|
65
|
+
const specIds = new Set();
|
|
66
|
+
for (const lesson of localBlockedLessons) {
|
|
67
|
+
const specId = normalizeString(lesson && lesson.spec_id);
|
|
68
|
+
if (specId) {
|
|
69
|
+
specIds.add(specId);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const receipt of [...mergeReceipts, ...releaseReceipts]) {
|
|
73
|
+
const specId = normalizeString(receipt && receipt.spec_id);
|
|
74
|
+
const blockedCount = normalizeInteger(receipt && receipt.totals && receipt.totals.blocked, 0);
|
|
75
|
+
if (specId && blockedCount > 0) {
|
|
76
|
+
specIds.add(specId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return [...specIds].sort((left, right) => left.localeCompare(right));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function buildLocalBlockedItems(lessons = [], ledgersByCapabilityId = {}, limit = 10) {
|
|
83
|
+
const ledgersByLessonId = {};
|
|
84
|
+
for (const ledger of Object.values(ledgersByCapabilityId || {})) {
|
|
85
|
+
const lessonId = normalizeString(ledger && ledger.lesson_id);
|
|
86
|
+
if (lessonId && !ledgersByLessonId[lessonId]) {
|
|
87
|
+
ledgersByLessonId[lessonId] = ledger;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return lessons
|
|
91
|
+
.map((lesson) => {
|
|
92
|
+
const publish = readLessonPublish(lesson);
|
|
93
|
+
const linkedLedger = ledgersByLessonId[normalizeString(lesson.lesson_id)] || null;
|
|
94
|
+
const capabilityId = normalizeString(linkedLedger && linkedLedger.capability_id);
|
|
95
|
+
const ledger = linkedLedger;
|
|
96
|
+
return {
|
|
97
|
+
lesson_id: lesson.lesson_id,
|
|
98
|
+
capability_id: capabilityId || null,
|
|
99
|
+
project_id: lesson.project_id || null,
|
|
100
|
+
channel_id: normalizeString(lesson.metadata && lesson.metadata.channel_id) || null,
|
|
101
|
+
session_id: normalizeString(lesson.metadata && lesson.metadata.session_id) || null,
|
|
102
|
+
scene_id: lesson.scene_id || null,
|
|
103
|
+
spec_id: lesson.spec_id || null,
|
|
104
|
+
blocked_reason: normalizeString(publish.blocked_reason) || 'unknown',
|
|
105
|
+
blocked_at: normalizeString(publish.blocked_at) || null,
|
|
106
|
+
governance_penalty_score: Number(ledger && ledger.metrics && ledger.metrics.governance_penalty_score) || 0,
|
|
107
|
+
governance_refuse_count: Number(ledger && ledger.metrics && ledger.metrics.governance_refuse_count) || 0,
|
|
108
|
+
governance_execution_blocked_count: Number(ledger && ledger.metrics && ledger.metrics.governance_execution_blocked_count) || 0
|
|
109
|
+
};
|
|
110
|
+
})
|
|
111
|
+
.sort((left, right) => `${right.blocked_at || ''}`.localeCompare(`${left.blocked_at || ''}`))
|
|
112
|
+
.slice(0, limit);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function buildSemanticBackflowReport(options = {}, dependencies = {}) {
|
|
116
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
117
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
118
|
+
const store = dependencies.stateStore || getSceStateStore(projectPath, {
|
|
119
|
+
fileSystem,
|
|
120
|
+
env: dependencies.env,
|
|
121
|
+
sqliteModule: dependencies.sqliteModule,
|
|
122
|
+
noCache: dependencies.noCache === true
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const projectId = normalizeString(options.project_id || options.projectId);
|
|
126
|
+
const specId = normalizeString(options.spec_id || options.specId);
|
|
127
|
+
const limit = normalizeInteger(options.limit, 200);
|
|
128
|
+
const recentLimit = normalizeInteger(options.recent_limit || options.recentLimit, 10);
|
|
129
|
+
|
|
130
|
+
let lessons = await store.listSemanticLessonCandidates({
|
|
131
|
+
project_id: projectId,
|
|
132
|
+
limit
|
|
133
|
+
});
|
|
134
|
+
lessons = Array.isArray(lessons) ? lessons : [];
|
|
135
|
+
if (specId) {
|
|
136
|
+
lessons = lessons.filter((item) => normalizeString(item.spec_id) === specId);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let ledgers = await store.listSemanticCapabilityLedgers({
|
|
140
|
+
project_id: projectId,
|
|
141
|
+
spec_id: specId,
|
|
142
|
+
limit
|
|
143
|
+
});
|
|
144
|
+
ledgers = Array.isArray(ledgers) ? ledgers : [];
|
|
145
|
+
const ledgersByCapabilityId = {};
|
|
146
|
+
for (const ledger of ledgers) {
|
|
147
|
+
const capabilityId = normalizeString(ledger.capability_id);
|
|
148
|
+
if (capabilityId) {
|
|
149
|
+
ledgersByCapabilityId[capabilityId] = ledger;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const localBlockedLessons = lessons.filter((item) => {
|
|
154
|
+
const publish = readLessonPublish(item);
|
|
155
|
+
return normalizeString(item.status) === 'publish_blocked'
|
|
156
|
+
|| normalizeString(publish.publish_state) === 'blocked';
|
|
157
|
+
});
|
|
158
|
+
const localBlockedByReason = {};
|
|
159
|
+
for (const lesson of localBlockedLessons) {
|
|
160
|
+
incrementCounter(localBlockedByReason, readLessonPublish(lesson).blocked_reason || 'unknown');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const targetSpecIds = listUniqueSpecIds(lessons, specId);
|
|
164
|
+
const mergeBlockedByReason = {};
|
|
165
|
+
const releaseBlockedByReason = {};
|
|
166
|
+
const mergeReceipts = [];
|
|
167
|
+
const releaseReceipts = [];
|
|
168
|
+
|
|
169
|
+
for (const currentSpecId of targetSpecIds) {
|
|
170
|
+
const mergeReceiptPath = path.join(projectPath, '.sce', 'specs', currentSpecId, 'registry', 'semantic-shared', 'latest.receipt.json');
|
|
171
|
+
const releaseReceiptPath = path.join(projectPath, '.sce', 'specs', currentSpecId, 'registry', 'semantic-shared', 'published', 'latest.receipt.json');
|
|
172
|
+
const mergeReceipt = await readJsonIfExists(mergeReceiptPath, fileSystem);
|
|
173
|
+
const releaseReceipt = await readJsonIfExists(releaseReceiptPath, fileSystem);
|
|
174
|
+
|
|
175
|
+
if (mergeReceipt) {
|
|
176
|
+
mergeReceipts.push({
|
|
177
|
+
spec_id: currentSpecId,
|
|
178
|
+
success: mergeReceipt.success === true,
|
|
179
|
+
generated_at: normalizeString(mergeReceipt.generated_at) || null,
|
|
180
|
+
totals: mergeReceipt.totals || {},
|
|
181
|
+
blocked: Array.isArray(mergeReceipt.blocked) ? mergeReceipt.blocked : []
|
|
182
|
+
});
|
|
183
|
+
for (const item of Array.isArray(mergeReceipt.blocked) ? mergeReceipt.blocked : []) {
|
|
184
|
+
incrementCounter(mergeBlockedByReason, item.reason || 'unknown');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (releaseReceipt) {
|
|
189
|
+
releaseReceipts.push({
|
|
190
|
+
spec_id: currentSpecId,
|
|
191
|
+
success: releaseReceipt.success === true,
|
|
192
|
+
generated_at: normalizeString(releaseReceipt.generated_at) || null,
|
|
193
|
+
totals: releaseReceipt.totals || {},
|
|
194
|
+
blocked: Array.isArray(releaseReceipt.blocked) ? releaseReceipt.blocked : []
|
|
195
|
+
});
|
|
196
|
+
for (const item of Array.isArray(releaseReceipt.blocked) ? releaseReceipt.blocked : []) {
|
|
197
|
+
incrementCounter(releaseBlockedByReason, item.reason || 'unknown');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const blockedSpecIds = collectBlockedSpecIds(localBlockedLessons, mergeReceipts, releaseReceipts);
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
mode: 'semantic-backflow-report',
|
|
206
|
+
success: true,
|
|
207
|
+
generated_at: new Date().toISOString(),
|
|
208
|
+
filters: {
|
|
209
|
+
project_id: projectId || null,
|
|
210
|
+
spec_id: specId || null,
|
|
211
|
+
limit,
|
|
212
|
+
recent_limit: recentLimit
|
|
213
|
+
},
|
|
214
|
+
totals: {
|
|
215
|
+
lesson_total: lessons.length,
|
|
216
|
+
local_publish_blocked: localBlockedLessons.length,
|
|
217
|
+
central_merge_blocked: mergeReceipts.reduce((sum, item) => sum + normalizeInteger(item.totals && item.totals.blocked, 0), 0),
|
|
218
|
+
central_release_blocked: releaseReceipts.reduce((sum, item) => sum + normalizeInteger(item.totals && item.totals.blocked, 0), 0),
|
|
219
|
+
blocked_spec_count: blockedSpecIds.length,
|
|
220
|
+
merge_receipt_count: mergeReceipts.length,
|
|
221
|
+
release_receipt_count: releaseReceipts.length
|
|
222
|
+
},
|
|
223
|
+
blocked_spec_ids: blockedSpecIds,
|
|
224
|
+
local: {
|
|
225
|
+
blocked_by_reason: sortCounters(localBlockedByReason, 'reason'),
|
|
226
|
+
recent_blocked_items: buildLocalBlockedItems(localBlockedLessons, ledgersByCapabilityId, recentLimit)
|
|
227
|
+
},
|
|
228
|
+
central_merge: {
|
|
229
|
+
blocked_by_reason: sortCounters(mergeBlockedByReason, 'reason'),
|
|
230
|
+
receipts: mergeReceipts
|
|
231
|
+
.sort((left, right) => `${right.generated_at || ''}`.localeCompare(`${left.generated_at || ''}`))
|
|
232
|
+
.slice(0, recentLimit)
|
|
233
|
+
},
|
|
234
|
+
central_release: {
|
|
235
|
+
blocked_by_reason: sortCounters(releaseBlockedByReason, 'reason'),
|
|
236
|
+
receipts: releaseReceipts
|
|
237
|
+
.sort((left, right) => `${right.generated_at || ''}`.localeCompare(`${left.generated_at || ''}`))
|
|
238
|
+
.slice(0, recentLimit)
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
module.exports = {
|
|
244
|
+
buildSemanticBackflowReport
|
|
245
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const { buildSemanticArchiveDecision } = require('./archive-routing');
|
|
2
|
+
|
|
3
|
+
function normalizeString(value) {
|
|
4
|
+
if (typeof value !== 'string') {
|
|
5
|
+
return '';
|
|
6
|
+
}
|
|
7
|
+
return value.trim();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function resolveTargetLibrary(lesson = {}) {
|
|
11
|
+
return normalizeString(buildSemanticArchiveDecision(lesson).target_library) || 'application-library';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function buildCapabilityId(lesson = {}) {
|
|
15
|
+
const base = normalizeString(lesson.fingerprint || lesson.lesson_id || 'capability');
|
|
16
|
+
const metadata = lesson.metadata && typeof lesson.metadata === 'object'
|
|
17
|
+
? lesson.metadata
|
|
18
|
+
: {};
|
|
19
|
+
const channelId = normalizeString(metadata.channel_id || metadata.channelId);
|
|
20
|
+
const normalizedBase = base.replace(/[^a-zA-Z0-9._-]+/g, '-').toLowerCase();
|
|
21
|
+
const normalizedChannel = channelId.replace(/[^a-zA-Z0-9._-]+/g, '-').toLowerCase();
|
|
22
|
+
return normalizedChannel
|
|
23
|
+
? `cap.${normalizedBase}.ch.${normalizedChannel}`
|
|
24
|
+
: `cap.${normalizedBase}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
resolveTargetLibrary,
|
|
29
|
+
buildCapabilityId
|
|
30
|
+
};
|