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,181 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const { ProjectChannelContextStore } = require('../runtime/project-channel-context-store');
|
|
3
|
+
|
|
4
|
+
function normalizeString(value) {
|
|
5
|
+
if (typeof value !== 'string') {
|
|
6
|
+
return '';
|
|
7
|
+
}
|
|
8
|
+
return value.trim();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function safeIsoAt(value) {
|
|
12
|
+
const normalized = normalizeString(value);
|
|
13
|
+
if (!normalized) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const time = Date.parse(normalized);
|
|
17
|
+
return Number.isFinite(time) ? new Date(time).toISOString() : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function buildEmptyProjectChannelProjection() {
|
|
21
|
+
return {
|
|
22
|
+
summary: {
|
|
23
|
+
available: false,
|
|
24
|
+
contextProjectId: null,
|
|
25
|
+
canonicalProjectIdMatched: false,
|
|
26
|
+
focusedChannelId: null,
|
|
27
|
+
channelCount: 0,
|
|
28
|
+
storageMode: 'none'
|
|
29
|
+
},
|
|
30
|
+
resolvedChannel: null,
|
|
31
|
+
channels: [],
|
|
32
|
+
partial: false,
|
|
33
|
+
partialReasons: []
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function sortChannels(channels = [], focusedChannelId = '') {
|
|
38
|
+
const normalizedFocusedChannelId = normalizeString(focusedChannelId);
|
|
39
|
+
return [...channels].sort((left, right) => {
|
|
40
|
+
const leftFocused = normalizeString(left && left.channelId) === normalizedFocusedChannelId ? 0 : 1;
|
|
41
|
+
const rightFocused = normalizeString(right && right.channelId) === normalizedFocusedChannelId ? 0 : 1;
|
|
42
|
+
if (leftFocused !== rightFocused) {
|
|
43
|
+
return leftFocused - rightFocused;
|
|
44
|
+
}
|
|
45
|
+
return `${left && left.channelId ? left.channelId : ''}`.localeCompare(`${right && right.channelId ? right.channelId : ''}`);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function buildChannelSummary(channel = {}, focusedChannelId = '') {
|
|
50
|
+
const channelId = normalizeString(channel.channelId);
|
|
51
|
+
return {
|
|
52
|
+
channelId,
|
|
53
|
+
focused: channelId === normalizeString(focusedChannelId),
|
|
54
|
+
activeScene: normalizeString(channel.activeScene) || null,
|
|
55
|
+
activeSpecId: normalizeString(channel.activeSpecId) || null,
|
|
56
|
+
activeDoc: normalizeString(channel.activeDoc) || null,
|
|
57
|
+
activeSessionPath: normalizeString(channel.activeSessionPath) || null,
|
|
58
|
+
runState: normalizeString(channel.runState) || null,
|
|
59
|
+
updatedAt: safeIsoAt(channel.updatedAt),
|
|
60
|
+
...(channel.agentRuntime && typeof channel.agentRuntime === 'object'
|
|
61
|
+
? {
|
|
62
|
+
agentRuntime: {
|
|
63
|
+
agentId: normalizeString(channel.agentRuntime.agentId) || null,
|
|
64
|
+
backendProfile: normalizeString(channel.agentRuntime.backendProfile) || null,
|
|
65
|
+
runtimeSessionId: normalizeString(channel.agentRuntime.runtimeSessionId) || null
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
: {})
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function listPersistedProjectChannelIds(projectRoot, fileSystem = fs) {
|
|
73
|
+
const store = new ProjectChannelContextStore(projectRoot, fileSystem);
|
|
74
|
+
const contexts = await store.listPersistedContexts();
|
|
75
|
+
return contexts.map((item) => item.projectId);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function chooseContextProjectId(projectIds = [], preferredProjectIds = []) {
|
|
79
|
+
const preferred = preferredProjectIds
|
|
80
|
+
.map((value) => normalizeString(value))
|
|
81
|
+
.filter(Boolean);
|
|
82
|
+
for (const preferredProjectId of preferred) {
|
|
83
|
+
if (projectIds.includes(preferredProjectId)) {
|
|
84
|
+
return {
|
|
85
|
+
projectId: preferredProjectId,
|
|
86
|
+
ambiguous: false
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (projectIds.length === 1) {
|
|
91
|
+
return {
|
|
92
|
+
projectId: projectIds[0],
|
|
93
|
+
ambiguous: false
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
projectId: null,
|
|
98
|
+
ambiguous: projectIds.length > 1
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function buildProjectChannelProjection(projectRoot, options = {}, dependencies = {}) {
|
|
103
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
104
|
+
const store = dependencies.projectChannelContextStore || new ProjectChannelContextStore(projectRoot, fileSystem);
|
|
105
|
+
const preferredProjectIds = Array.isArray(options.preferredProjectIds)
|
|
106
|
+
? options.preferredProjectIds
|
|
107
|
+
: [];
|
|
108
|
+
const requestedChannelId = normalizeString(options.channelId);
|
|
109
|
+
const partialReasons = [];
|
|
110
|
+
|
|
111
|
+
let projectIds = [];
|
|
112
|
+
let persistedContexts = [];
|
|
113
|
+
try {
|
|
114
|
+
persistedContexts = await store.listPersistedContexts();
|
|
115
|
+
projectIds = persistedContexts.map((item) => item.projectId);
|
|
116
|
+
} catch (_error) {
|
|
117
|
+
partialReasons.push('project_channel_context_unavailable');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const resolution = chooseContextProjectId(projectIds, preferredProjectIds);
|
|
121
|
+
if (resolution.ambiguous) {
|
|
122
|
+
partialReasons.push('project_channel_context_ambiguous');
|
|
123
|
+
}
|
|
124
|
+
if (!resolution.projectId) {
|
|
125
|
+
return {
|
|
126
|
+
...buildEmptyProjectChannelProjection(),
|
|
127
|
+
partial: partialReasons.length > 0,
|
|
128
|
+
partialReasons
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let context;
|
|
133
|
+
try {
|
|
134
|
+
context = await store.load(resolution.projectId, {
|
|
135
|
+
allowDefaultChannelSynthesis: false
|
|
136
|
+
});
|
|
137
|
+
} catch (_error) {
|
|
138
|
+
return {
|
|
139
|
+
...buildEmptyProjectChannelProjection(),
|
|
140
|
+
partial: true,
|
|
141
|
+
partialReasons: [...partialReasons, 'project_channel_context_unavailable']
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const focusedChannelId = normalizeString(context.focusedChannelId) || null;
|
|
146
|
+
const channels = sortChannels(
|
|
147
|
+
Object.values(context.channels || {}).map((channel) => buildChannelSummary(channel, focusedChannelId)),
|
|
148
|
+
focusedChannelId
|
|
149
|
+
);
|
|
150
|
+
if (channels.length === 0) {
|
|
151
|
+
partialReasons.push('project_channel_context_empty');
|
|
152
|
+
}
|
|
153
|
+
const resolvedChannel = channels.find((channel) => channel.channelId === requestedChannelId)
|
|
154
|
+
|| channels.find((channel) => channel.focused)
|
|
155
|
+
|| null;
|
|
156
|
+
const contextProjectId = normalizeString(context.projectId) || resolution.projectId;
|
|
157
|
+
const canonicalProjectIdMatched = preferredProjectIds.includes(contextProjectId);
|
|
158
|
+
const storageRecord = persistedContexts.find((item) => item.projectId === contextProjectId)
|
|
159
|
+
|| persistedContexts.find((item) => item.projectId === resolution.projectId)
|
|
160
|
+
|| null;
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
summary: {
|
|
164
|
+
available: true,
|
|
165
|
+
contextProjectId,
|
|
166
|
+
canonicalProjectIdMatched,
|
|
167
|
+
focusedChannelId,
|
|
168
|
+
channelCount: channels.length,
|
|
169
|
+
storageMode: storageRecord ? storageRecord.storageMode : 'unknown'
|
|
170
|
+
},
|
|
171
|
+
resolvedChannel,
|
|
172
|
+
channels,
|
|
173
|
+
partial: partialReasons.length > 0,
|
|
174
|
+
partialReasons
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = {
|
|
179
|
+
buildEmptyProjectChannelProjection,
|
|
180
|
+
buildProjectChannelProjection
|
|
181
|
+
};
|
|
@@ -3,6 +3,9 @@ const fs = require('fs-extra');
|
|
|
3
3
|
const WorkspaceStateManager = require('../workspace/multi/workspace-state-manager');
|
|
4
4
|
const SmartOrchestrator = require('../adoption/smart-orchestrator');
|
|
5
5
|
const { applyTakeoverBaseline } = require('../workspace/takeover-baseline');
|
|
6
|
+
const { discoverSemanticSharedSourceDescriptors } = require('../semantic/shared-source-discovery');
|
|
7
|
+
const { buildProjectChannelProjection } = require('./project-channel-projection');
|
|
8
|
+
const { buildProjectChannelOutputFromProjection } = require('./project-channel-output');
|
|
6
9
|
const {
|
|
7
10
|
PROJECT_CANDIDATE_REASON_CODES,
|
|
8
11
|
inspectProjectCandidate
|
|
@@ -52,6 +55,19 @@ function buildPublication(preview = {}, options = {}) {
|
|
|
52
55
|
};
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
function buildSemanticSharedSourceDiscoverySummary(discovery = {}) {
|
|
59
|
+
const summary = discovery && typeof discovery.summary === 'object'
|
|
60
|
+
? discovery.summary
|
|
61
|
+
: {};
|
|
62
|
+
return {
|
|
63
|
+
totalDescriptors: Number.isFinite(summary.total) ? summary.total : 0,
|
|
64
|
+
approvedDescriptors: Number.isFinite(summary.approved) ? summary.approved : 0,
|
|
65
|
+
blockedDescriptors: Number.isFinite(summary.blocked) ? summary.blocked : 0,
|
|
66
|
+
items: Array.isArray(discovery.items) ? discovery.items : [],
|
|
67
|
+
blocked: Array.isArray(discovery.blocked) ? discovery.blocked : []
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
55
71
|
function buildWorkspaceNameCandidate(rootDir) {
|
|
56
72
|
const base = path.basename(rootDir).trim().toLowerCase();
|
|
57
73
|
const normalized = base.replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
@@ -129,29 +145,43 @@ async function runProjectRootOnboardingImport(options = {}, dependencies = {}) {
|
|
|
129
145
|
const steps = [];
|
|
130
146
|
|
|
131
147
|
if (rootInspection.kind === 'invalid') {
|
|
148
|
+
const blockedReasonCode = rootInspection.reasonCodes.includes(PROJECT_CANDIDATE_REASON_CODES.EPHEMERAL_ROOT)
|
|
149
|
+
? PROJECT_CANDIDATE_REASON_CODES.EPHEMERAL_ROOT
|
|
150
|
+
: PROJECT_ONBOARDING_REASON_CODES.BLOCKED_BY_CANDIDATE;
|
|
151
|
+
const attachDetail = rootInspection.reasonCodes.includes(PROJECT_CANDIDATE_REASON_CODES.EPHEMERAL_ROOT)
|
|
152
|
+
? 'Local root is reserved for ephemeral probe/import flow use and cannot become a managed project.'
|
|
153
|
+
: 'Local root is not accessible as a project directory.';
|
|
154
|
+
const publishDetail = rootInspection.reasonCodes.includes(PROJECT_CANDIDATE_REASON_CODES.EPHEMERAL_ROOT)
|
|
155
|
+
? 'Canonical portfolio publication is blocked for ephemeral probe roots.'
|
|
156
|
+
: 'Canonical portfolio publication is blocked until the root is valid.';
|
|
157
|
+
const failureDetail = rootInspection.reasonCodes.includes(PROJECT_CANDIDATE_REASON_CODES.EPHEMERAL_ROOT)
|
|
158
|
+
? 'Root inspection reported an ephemeral probe root that must not be imported.'
|
|
159
|
+
: 'Root inspection reported an invalid candidate state.';
|
|
132
160
|
steps.push(buildStep(
|
|
133
161
|
'register',
|
|
134
162
|
'failed',
|
|
135
163
|
'Root directory cannot be onboarded.',
|
|
136
|
-
|
|
164
|
+
blockedReasonCode
|
|
137
165
|
));
|
|
138
166
|
steps.push(buildStep(
|
|
139
167
|
'attach',
|
|
140
168
|
'failed',
|
|
141
|
-
|
|
142
|
-
PROJECT_CANDIDATE_REASON_CODES.
|
|
169
|
+
attachDetail,
|
|
170
|
+
rootInspection.reasonCodes.includes(PROJECT_CANDIDATE_REASON_CODES.EPHEMERAL_ROOT)
|
|
171
|
+
? PROJECT_CANDIDATE_REASON_CODES.EPHEMERAL_ROOT
|
|
172
|
+
: PROJECT_CANDIDATE_REASON_CODES.ROOT_INACCESSIBLE
|
|
143
173
|
));
|
|
144
174
|
steps.push(buildStep(
|
|
145
175
|
'hydrate',
|
|
146
176
|
'failed',
|
|
147
177
|
'Onboarding cannot continue until the root is valid.',
|
|
148
|
-
|
|
178
|
+
blockedReasonCode
|
|
149
179
|
));
|
|
150
180
|
steps.push(buildStep(
|
|
151
181
|
'publish',
|
|
152
182
|
'skipped',
|
|
153
|
-
|
|
154
|
-
|
|
183
|
+
publishDetail,
|
|
184
|
+
blockedReasonCode
|
|
155
185
|
));
|
|
156
186
|
steps.push(buildStep(
|
|
157
187
|
'activate',
|
|
@@ -168,8 +198,8 @@ async function runProjectRootOnboardingImport(options = {}, dependencies = {}) {
|
|
|
168
198
|
return buildFailureEnvelope(
|
|
169
199
|
rootInspection,
|
|
170
200
|
steps,
|
|
171
|
-
|
|
172
|
-
|
|
201
|
+
failureDetail,
|
|
202
|
+
blockedReasonCode
|
|
173
203
|
);
|
|
174
204
|
}
|
|
175
205
|
|
|
@@ -358,6 +388,17 @@ async function runProjectRootOnboardingImport(options = {}, dependencies = {}) {
|
|
|
358
388
|
: PROJECT_ONBOARDING_REASON_CODES.SCAFFOLD_REUSED
|
|
359
389
|
));
|
|
360
390
|
|
|
391
|
+
const semanticSharedSourceDiscovery = buildSemanticSharedSourceDiscoverySummary(
|
|
392
|
+
await discoverSemanticSharedSourceDescriptors(rootInspection.rootDir, {
|
|
393
|
+
fileSystem
|
|
394
|
+
})
|
|
395
|
+
);
|
|
396
|
+
const projectChannelProjection = await buildProjectChannelProjection(rootInspection.rootDir, {
|
|
397
|
+
preferredProjectIds: [onboardingPreview.projectId, onboardingPreview.workspaceId]
|
|
398
|
+
}, {
|
|
399
|
+
fileSystem
|
|
400
|
+
});
|
|
401
|
+
|
|
361
402
|
return {
|
|
362
403
|
mode: 'import',
|
|
363
404
|
generated_at: publishedAt,
|
|
@@ -369,6 +410,17 @@ async function runProjectRootOnboardingImport(options = {}, dependencies = {}) {
|
|
|
369
410
|
visibleInPortfolio: true,
|
|
370
411
|
publishedAt
|
|
371
412
|
}),
|
|
413
|
+
semanticSharedSourceDiscovery,
|
|
414
|
+
projectChannelContext: {
|
|
415
|
+
...projectChannelProjection.summary
|
|
416
|
+
},
|
|
417
|
+
...(projectChannelProjection.resolvedChannel
|
|
418
|
+
? {
|
|
419
|
+
projectChannel: buildProjectChannelOutputFromProjection(projectChannelProjection, {
|
|
420
|
+
canonicalProjectId: onboardingPreview.projectId || null
|
|
421
|
+
})
|
|
422
|
+
}
|
|
423
|
+
: {}),
|
|
372
424
|
steps,
|
|
373
425
|
result: {
|
|
374
426
|
rootDir: onboardingPreview.rootDir,
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const { listSemanticSharedSources } = require('../semantic/shared-source-config');
|
|
4
|
+
const { discoverSemanticSharedSourceDescriptors } = require('../semantic/shared-source-discovery');
|
|
5
|
+
|
|
6
|
+
function normalizeString(value) {
|
|
7
|
+
if (typeof value !== 'string') {
|
|
8
|
+
return '';
|
|
9
|
+
}
|
|
10
|
+
return value.trim();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function buildSourceKey(name = '', bundle = '') {
|
|
14
|
+
const normalizedName = normalizeString(name) || 'semantic-shared';
|
|
15
|
+
const normalizedBundle = normalizeString(bundle);
|
|
16
|
+
return `${normalizedName}::${normalizedBundle}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function buildConfiguredSourceIndexes(items = []) {
|
|
20
|
+
const byKey = new Set();
|
|
21
|
+
const byName = new Set();
|
|
22
|
+
for (const item of items) {
|
|
23
|
+
const name = normalizeString(item && item.name) || 'semantic-shared';
|
|
24
|
+
const bundle = normalizeString(item && item.bundle);
|
|
25
|
+
byName.add(name);
|
|
26
|
+
if (bundle) {
|
|
27
|
+
byKey.add(buildSourceKey(name, bundle));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
byKey,
|
|
32
|
+
byName
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isDescriptorConnected(item = {}, configuredIndexes = {}) {
|
|
37
|
+
if (!item.approved) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const name = normalizeString(item.source_name) || 'semantic-shared';
|
|
41
|
+
const bundle = normalizeString(item.bundle);
|
|
42
|
+
if (bundle && configuredIndexes.byKey instanceof Set && configuredIndexes.byKey.has(buildSourceKey(name, bundle))) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return !bundle
|
|
46
|
+
&& configuredIndexes.byName instanceof Set
|
|
47
|
+
&& configuredIndexes.byName.has(name);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function buildEmptySummary() {
|
|
51
|
+
return {
|
|
52
|
+
totalDescriptors: 0,
|
|
53
|
+
approvedDescriptors: 0,
|
|
54
|
+
blockedDescriptors: 0,
|
|
55
|
+
configuredSources: 0,
|
|
56
|
+
connectedApprovedDescriptors: 0,
|
|
57
|
+
pendingApprovedDescriptors: 0,
|
|
58
|
+
hasApprovedDescriptors: false,
|
|
59
|
+
hasPendingApprovedDescriptors: false
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function readDescriptorUpdatedAt(projectRoot, descriptorFile, fileSystem = fs) {
|
|
64
|
+
const normalizedDescriptorFile = normalizeString(descriptorFile);
|
|
65
|
+
if (!normalizedDescriptorFile) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const absolutePath = path.join(projectRoot, normalizedDescriptorFile);
|
|
69
|
+
try {
|
|
70
|
+
const stats = await fileSystem.stat(absolutePath);
|
|
71
|
+
if (stats && stats.mtime instanceof Date && Number.isFinite(stats.mtime.getTime())) {
|
|
72
|
+
return stats.mtime.toISOString();
|
|
73
|
+
}
|
|
74
|
+
} catch (_error) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function buildSemanticSharedSourceProjection(projectRoot, dependencies = {}) {
|
|
81
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
82
|
+
const partialReasons = [];
|
|
83
|
+
let discovery = {
|
|
84
|
+
summary: {
|
|
85
|
+
total: 0,
|
|
86
|
+
approved: 0,
|
|
87
|
+
blocked: 0
|
|
88
|
+
},
|
|
89
|
+
items: [],
|
|
90
|
+
blocked: []
|
|
91
|
+
};
|
|
92
|
+
let configuredSources = {
|
|
93
|
+
total: 0,
|
|
94
|
+
items: []
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
discovery = await discoverSemanticSharedSourceDescriptors(projectRoot, {
|
|
99
|
+
fileSystem
|
|
100
|
+
});
|
|
101
|
+
} catch (_error) {
|
|
102
|
+
partialReasons.push('semantic_shared_source_discovery_unavailable');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
configuredSources = await listSemanticSharedSources({}, {
|
|
107
|
+
projectPath: projectRoot,
|
|
108
|
+
fileSystem
|
|
109
|
+
});
|
|
110
|
+
} catch (_error) {
|
|
111
|
+
partialReasons.push('semantic_shared_source_config_unavailable');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const configuredItems = Array.isArray(configuredSources.items) ? configuredSources.items : [];
|
|
115
|
+
const configuredIndexes = buildConfiguredSourceIndexes(configuredItems);
|
|
116
|
+
const sourceItems = Array.isArray(discovery.items) ? discovery.items : [];
|
|
117
|
+
const items = [];
|
|
118
|
+
for (const item of sourceItems) {
|
|
119
|
+
items.push({
|
|
120
|
+
...item,
|
|
121
|
+
connected: isDescriptorConnected(item, configuredIndexes),
|
|
122
|
+
updated_at: await readDescriptorUpdatedAt(projectRoot, item.descriptor_file, fileSystem)
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const approvedItems = items.filter((item) => item.approved);
|
|
126
|
+
const connectedApprovedItems = approvedItems.filter((item) => item.connected);
|
|
127
|
+
const pendingApprovedItems = approvedItems.filter((item) => !item.connected);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
summary: {
|
|
131
|
+
totalDescriptors: items.length,
|
|
132
|
+
approvedDescriptors: approvedItems.length,
|
|
133
|
+
blockedDescriptors: Array.isArray(discovery.blocked) ? discovery.blocked.length : 0,
|
|
134
|
+
configuredSources: configuredItems.length,
|
|
135
|
+
connectedApprovedDescriptors: connectedApprovedItems.length,
|
|
136
|
+
pendingApprovedDescriptors: pendingApprovedItems.length,
|
|
137
|
+
hasApprovedDescriptors: approvedItems.length > 0,
|
|
138
|
+
hasPendingApprovedDescriptors: pendingApprovedItems.length > 0
|
|
139
|
+
},
|
|
140
|
+
items,
|
|
141
|
+
blocked: Array.isArray(discovery.blocked) ? discovery.blocked : [],
|
|
142
|
+
partial: partialReasons.length > 0,
|
|
143
|
+
partialReasons
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = {
|
|
148
|
+
buildEmptySummary,
|
|
149
|
+
buildSemanticSharedSourceProjection
|
|
150
|
+
};
|