@bpmsoftwaresolutions/ai-engine-client 1.1.87 → 1.1.88

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/src/client.js CHANGED
@@ -16,10 +16,14 @@ import { createHealthDomain } from './domains/health.js';
16
16
  import { createLogaDomain } from './domains/loga.js';
17
17
  import { createNotesLabDomain } from './domains/notes-lab.js';
18
18
  import { createPortfolioDomain } from './domains/portfolio.js';
19
+ import { createProjectReportsDomain } from './domains/project-reports.js';
20
+ import { createProjectResumeDomain } from './domains/project-resume.js';
19
21
  import { createProjectionsDomain } from './domains/projections.js';
20
22
  import { createReportsDomain } from './domains/reports.js';
23
+ import { createRoadmapReportsDomain } from './domains/roadmap-reports.js';
21
24
  import { createPerformanceDomain } from './domains/performance.js';
22
25
  import { createImplementationArtifactsDomain } from './domains/implementation-artifacts.js';
26
+ import { createImplementationChecksDomain } from './domains/implementation-checks.js';
23
27
  import { createImplementationEvidenceDomain } from './domains/implementation-evidence.js';
24
28
  import { createImplementationGatesDomain } from './domains/implementation-gates.js';
25
29
  import { createImplementationPacketsDomain } from './domains/implementation-packets.js';
@@ -51,6 +55,8 @@ import { createWorkflowsDomain } from './domains/workflows.js';
51
55
  import { createWarehouseDomain } from './domains/warehouse.js';
52
56
  import { buildHeaders, requestBinary, requestJson, requestLogaProjection, requestText, resolveAccessToken } from './transport/index.js';
53
57
  import { normalizeEnum, trimTrailingSlash } from './utils/text.js';
58
+ import { normalizeMetadataTaskBinding, normalizeTaskBindingPolicy } from './utils/task-binding.js';
59
+ import { executeVerifiedMutation } from './utils/verified-mutations.js';
54
60
 
55
61
  function normalizeThreadType(value) {
56
62
  return normalizeEnum(value, AGENT_COMMUNICATION_THREAD_TYPES, 'coordination', 'thread_type', {
@@ -219,37 +225,6 @@ function compareSemanticVersions(left, right) {
219
225
  return 0;
220
226
  }
221
227
 
222
- function looksLikeUuid(value) {
223
- const text = cleanText(value);
224
- return Boolean(text && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(text));
225
- }
226
-
227
- function countRoadmapProjectionLines(markdown) {
228
- const lines = [];
229
- let inSummary = false;
230
- for (const line of String(markdown || '').split(/\r?\n/)) {
231
- if (line.startsWith('## Roadmap Summary')) {
232
- inSummary = true;
233
- continue;
234
- }
235
- if (inSummary && line.startsWith('## ')) {
236
- break;
237
- }
238
- if (inSummary && line.startsWith('- ')) {
239
- lines.push(line);
240
- }
241
- }
242
- const wanted = [
243
- 'Total Items',
244
- 'Total Tasks',
245
- 'Total Subtasks',
246
- 'Total Acceptance Checks',
247
- 'Open Acceptance Checks',
248
- 'Completion Percentage',
249
- ];
250
- return lines.filter((line) => wanted.some((label) => line.startsWith(`- ${label}:`)));
251
- }
252
-
253
228
  function cleanList(value) {
254
229
  if (!Array.isArray(value)) return [];
255
230
  return value.map((item) => cleanText(item)).filter(Boolean);
@@ -259,35 +234,6 @@ function isPlainObject(value) {
259
234
  return value !== null && typeof value === 'object' && !Array.isArray(value);
260
235
  }
261
236
 
262
- function normalizeTaskBindingPolicy(taskBinding, { expectedAction = null } = {}) {
263
- if (!isPlainObject(taskBinding)) return taskBinding;
264
- const taskId = cleanText(taskBinding.task_id || taskBinding.implementation_item_task_id);
265
- const roadmapItemId = cleanText(taskBinding.roadmap_item_id || taskBinding.implementation_item_id);
266
- const taskStatus = cleanText(taskBinding.task_status || taskBinding.status);
267
- const allowedActions = cleanList(taskBinding.allowed_actions);
268
- const substrateAction = cleanText(taskBinding.substrate_action) || cleanText(expectedAction);
269
- return {
270
- ...taskBinding,
271
- ...(taskId ? { task_id: taskId } : {}),
272
- ...(roadmapItemId ? { roadmap_item_id: roadmapItemId, implementation_item_id: roadmapItemId } : {}),
273
- ...(taskStatus ? { task_status: taskStatus } : {}),
274
- ...(allowedActions.length > 0 ? { allowed_actions: allowedActions } : {}),
275
- ...(substrateAction ? { substrate_action: substrateAction } : {}),
276
- policy_key: cleanText(taskBinding.policy_key) || 'task_bound_substrate_execution',
277
- policy_version: cleanText(taskBinding.policy_version) || 'v1',
278
- policy_identifier: cleanText(taskBinding.policy_identifier) || TASK_BOUND_SUBSTRATE_EXECUTION_POLICY,
279
- };
280
- }
281
-
282
- function normalizeMetadataTaskBinding(metadata, { expectedAction = null } = {}) {
283
- if (!isPlainObject(metadata)) return metadata;
284
- if (!isPlainObject(metadata.task_binding)) return metadata;
285
- return {
286
- ...metadata,
287
- task_binding: normalizeTaskBindingPolicy(metadata.task_binding, { expectedAction }),
288
- };
289
- }
290
-
291
237
  function isActiveBinding(binding) {
292
238
  return isPlainObject(binding) && (
293
239
  binding.is_active === true ||
@@ -333,28 +279,6 @@ function contextSessionIdFromInput(input) {
333
279
  return cleanText(input);
334
280
  }
335
281
 
336
- function matchesExpectedState(actual, expected) {
337
- if (typeof expected === 'function') {
338
- throw new Error('matchesExpectedState does not support function expectations.');
339
- }
340
- if (Array.isArray(expected)) {
341
- if (!Array.isArray(actual) || actual.length < expected.length) return false;
342
- return expected.every((item, index) => matchesExpectedState(actual[index], item));
343
- }
344
- if (isPlainObject(expected)) {
345
- if (!isPlainObject(actual)) return false;
346
- return Object.entries(expected).every(([key, value]) => matchesExpectedState(actual[key], value));
347
- }
348
- return Object.is(actual, expected);
349
- }
350
-
351
- function buildVerificationError(message, details = {}) {
352
- const error = new Error(message);
353
- error.code = 'POST_CONDITION_VERIFICATION_FAILED';
354
- error.details = details;
355
- return error;
356
- }
357
-
358
282
  function buildEligibilityError(message, details = {}) {
359
283
  const error = new Error(message);
360
284
  error.code = 'EXECUTION_ELIGIBILITY_FAILED';
@@ -388,10 +312,14 @@ export class AIEngineClient {
388
312
  this.database = createDatabaseDomain(this);
389
313
  this.projects = createProjectsDomain(this);
390
314
  this.projectChartering = createProjectCharteringDomain(this);
315
+ this.projectReports = createProjectReportsDomain(this);
316
+ this.projectResume = createProjectResumeDomain(this);
391
317
  this.roadmaps = createRoadmapsDomain(this);
318
+ this.roadmapReports = createRoadmapReportsDomain(this);
392
319
  this.implementationPackets = createImplementationPacketsDomain(this);
393
320
  this.implementationItems = createImplementationItemsDomain(this);
394
321
  this.implementationArtifacts = createImplementationArtifactsDomain(this);
322
+ this.implementationChecks = createImplementationChecksDomain(this);
395
323
  this.implementationEvidence = createImplementationEvidenceDomain(this);
396
324
  this.implementationGates = createImplementationGatesDomain(this);
397
325
  this.implementationTasks = createImplementationTasksDomain(this);
@@ -3624,19 +3552,7 @@ export class AIEngineClient {
3624
3552
  }
3625
3553
 
3626
3554
  async routeImplementationItem(implementationItemId, body = {}) {
3627
- if (!implementationItemId) throw new Error('implementationItemId is required.');
3628
- const normalizedBody = isPlainObject(body)
3629
- ? {
3630
- ...body,
3631
- ...(isPlainObject(body.task_binding)
3632
- ? { task_binding: normalizeTaskBindingPolicy(body.task_binding) }
3633
- : {}),
3634
- }
3635
- : body;
3636
- return this._request(`/api/governed-implementation/items/${encodeURIComponent(implementationItemId)}/routing`, {
3637
- method: 'PATCH',
3638
- body: normalizedBody,
3639
- });
3555
+ return this.projectChartering.routeImplementationItem(implementationItemId, body);
3640
3556
  }
3641
3557
 
3642
3558
  async persistAssistantTurn(body) {
@@ -4008,93 +3924,38 @@ export class AIEngineClient {
4008
3924
  return this.projects.listProjectWorkflowRuns(projectId, { limit });
4009
3925
  }
4010
3926
 
4011
- async closeProject(projectId, {
4012
- workflowId,
4013
- workflowRunIds,
4014
- reason,
4015
- operatorIdentity,
4016
- runTerminalStatus = 'failed',
4017
- } = {}) {
4018
- const normalizedProjectId = cleanText(projectId);
4019
- if (!normalizedProjectId) {
4020
- throw new Error('projectId is required.');
4021
- }
4022
- const normalizedReason = cleanText(reason) || 'close active project';
4023
- const normalizedOperatorIdentity = cleanText(operatorIdentity) || this.actorId;
4024
- const resolvedRuns = Array.isArray(workflowRunIds) ? cleanList(workflowRunIds) : [];
4025
- let resolvedWorkflowId = cleanText(workflowId);
4026
-
4027
- if (!resolvedWorkflowId || resolvedRuns.length === 0) {
4028
- const projectPayload = await this.getProject(normalizedProjectId);
4029
- const summary = isPlainObject(projectPayload?.summary) ? projectPayload.summary : {};
4030
- if (!resolvedWorkflowId) {
4031
- resolvedWorkflowId = cleanText(summary.workflow_id) || cleanText(summary.workflowId);
4032
- }
4033
- if (resolvedRuns.length === 0) {
4034
- const workflowRuns = await this.listProjectWorkflowRuns(normalizedProjectId, { limit: 100 });
4035
- const activeStatuses = new Set(['queued', 'running', 'blocked', 'open', 'active', 'ping-review']);
4036
- const activeRuns = Array.isArray(workflowRuns)
4037
- ? workflowRuns.filter((run) => activeStatuses.has(String(run?.status || '').trim().toLowerCase()))
4038
- : [];
4039
- for (const run of activeRuns) {
4040
- const runId = cleanText(run.workflow_run_id || run.workflowRunId);
4041
- if (runId && !resolvedRuns.includes(runId)) {
4042
- resolvedRuns.push(runId);
4043
- }
4044
- if (!resolvedWorkflowId) {
4045
- resolvedWorkflowId = cleanText(run.workflow_id || run.workflowId) || resolvedWorkflowId;
4046
- }
4047
- }
4048
- }
4049
- }
4050
-
4051
- if (resolvedRuns.length > 0 && !resolvedWorkflowId) {
4052
- throw new Error('workflowId could not be resolved for project cleanup.');
4053
- }
4054
-
4055
- const cleanupPayload = {
4056
- project_id: normalizedProjectId,
4057
- reason: normalizedReason,
4058
- operator_identity: normalizedOperatorIdentity,
4059
- run_terminal_status: cleanText(runTerminalStatus) || 'failed',
4060
- };
4061
- if (resolvedWorkflowId) {
4062
- cleanupPayload.workflow_id = resolvedWorkflowId;
4063
- }
4064
- if (resolvedRuns.length > 0) {
4065
- cleanupPayload.workflow_run_ids = resolvedRuns;
4066
- }
4067
-
4068
- return this._request('/api/operator/cleanup', {
4069
- method: 'POST',
4070
- body: cleanupPayload,
4071
- });
3927
+ async closeProject(projectId, options = {}) {
3928
+ return this.projects.closeProject(projectId, options);
4072
3929
  }
4073
3930
 
4074
3931
  async closeActiveProject(projectId, options = {}) {
4075
- return this.closeProject(projectId, options);
3932
+ return this.projects.closeActiveProject(projectId, options);
4076
3933
  }
4077
3934
 
4078
3935
  async getProjectCharterReport(projectId) {
4079
- return this.projects.getProjectCharterReport(projectId);
3936
+ return this.projectReports.getProjectCharterReport(projectId);
4080
3937
  }
4081
3938
 
4082
3939
  async createProjectMarkdownDownload(projectId, { reportType, includeMarkdown = false } = {}) {
4083
- return this.projects.createProjectMarkdownDownload(projectId, { reportType, includeMarkdown });
3940
+ return this.projectReports.createProjectMarkdownDownload(projectId, { reportType, includeMarkdown });
4084
3941
  }
4085
3942
 
4086
3943
  async downloadProjectMarkdownReport(projectId, reportType) {
4087
- return this.projects.downloadProjectMarkdownReport(projectId, reportType);
3944
+ return this.projectReports.downloadProjectMarkdownReport(projectId, reportType);
4088
3945
  }
4089
3946
 
4090
3947
  async downloadProjectCharterReportMarkdown(projectId) {
4091
- return this.projects.downloadProjectCharterReportMarkdown(projectId);
3948
+ return this.projectReports.downloadProjectCharterReportMarkdown(projectId);
4092
3949
  }
4093
3950
 
4094
3951
  async getProjectBundle(projectId) {
4095
3952
  return this.projects.getProjectBundle(projectId);
4096
3953
  }
4097
3954
 
3955
+ async listProjectOpenTasks(projectId) {
3956
+ return this.projects.listProjectOpenTasks(projectId);
3957
+ }
3958
+
4098
3959
  async resumeProjectWork({
4099
3960
  projectIdentifier,
4100
3961
  projectId,
@@ -4103,7 +3964,7 @@ export class AIEngineClient {
4103
3964
  requireClaim,
4104
3965
  workflowRunLimit,
4105
3966
  } = {}) {
4106
- return this.projects.resumeProjectWork({
3967
+ return this.projectResume.resumeProjectWork({
4107
3968
  projectIdentifier,
4108
3969
  projectId,
4109
3970
  actorMode,
@@ -4113,283 +3974,8 @@ export class AIEngineClient {
4113
3974
  });
4114
3975
  }
4115
3976
 
4116
- async closeRoadmapItemWorkflow({
4117
- projectIdentifier,
4118
- projectId,
4119
- claimId,
4120
- claimName,
4121
- actorId,
4122
- actorMode = 'operator',
4123
- executionIntent = 'close roadmap item workflow',
4124
- workflowRunLimit = 5,
4125
- declaredScopeFiles,
4126
- allowedMutationSurfaces,
4127
- requiredArtifacts,
4128
- requiredAcceptanceCheckStatus = 'verified',
4129
- terminalItemStatus = 'accepted',
4130
- gateType = 'roadmap_closure',
4131
- gateDecision = 'pass',
4132
- gateRationale,
4133
- gateEvidenceRefs,
4134
- remediationActions = [],
4135
- closureEvidenceType = 'roadmap_closure',
4136
- closureEvidenceRef,
4137
- closureEvidenceTitle,
4138
- closeProjectIfNoRemainingOpenItems = true,
4139
- closeProjectReason,
4140
- } = {}) {
4141
- const normalizedProjectReference = cleanText(projectIdentifier) || cleanText(projectId);
4142
- if (!normalizedProjectReference) {
4143
- throw new Error('projectIdentifier is required.');
4144
- }
4145
-
4146
- const normalizedActorId = cleanText(actorId) || this.actorId;
4147
- const continuation = await this.resumeProjectWork({
4148
- projectIdentifier: normalizedProjectReference,
4149
- projectId: normalizedProjectReference,
4150
- actorMode,
4151
- executionIntent,
4152
- requireClaim: false,
4153
- workflowRunLimit,
4154
- });
4155
-
4156
- const projectSummary = isPlainObject(continuation?.project) ? continuation.project : {};
4157
- const resolvedProjectId = cleanText(projectSummary.project_id) || cleanText(projectSummary.projectId) || normalizedProjectReference;
4158
- const resolvedWorkflowId = cleanText(projectSummary.workflow_id) || cleanText(projectSummary.workflowId);
4159
- const resolvedWorkflowRunId = cleanText(projectSummary.workflow_run_id) || cleanText(projectSummary.workflowRunId) || null;
4160
- const activeItem = await this.getProjectRoadmapActiveItem(resolvedProjectId);
4161
- const normalizedActiveItem = isPlainObject(activeItem) ? activeItem : {};
4162
- const implementationItemId = cleanText(normalizedActiveItem.implementation_item_id) || cleanText(normalizedActiveItem.implementationItemId);
4163
- const itemKey = cleanText(normalizedActiveItem.item_key) || cleanText(normalizedActiveItem.itemKey) || implementationItemId;
4164
- const itemStatus = cleanText(normalizedActiveItem.item_status) || cleanText(normalizedActiveItem.status);
4165
- const packetId = cleanText(normalizedActiveItem.implementation_packet_id) || cleanText(normalizedActiveItem.implementationPacketId);
4166
-
4167
- if (!implementationItemId || !resolvedWorkflowId || !packetId) {
4168
- throw new Error('project closure state is ambiguous.');
4169
- }
4170
-
4171
- let effectiveClaimId = cleanText(claimId);
4172
- let claimSource = 'provided';
4173
- if (effectiveClaimId) {
4174
- const claimStillValid = await this.claimIsValid(effectiveClaimId).catch(() => false);
4175
- if (!claimStillValid) {
4176
- effectiveClaimId = null;
4177
- claimSource = 'fresh';
4178
- }
4179
- } else {
4180
- claimSource = 'fresh';
4181
- }
4182
-
4183
- let freshClaim = null;
4184
- if (!effectiveClaimId) {
4185
- const declaredScope = cleanList(declaredScopeFiles);
4186
- const surfaces = cleanList(allowedMutationSurfaces);
4187
- const fallbackDeclaredScope = declaredScope.length > 0 ? declaredScope : [
4188
- `${resolvedProjectId}`,
4189
- itemKey || implementationItemId,
4190
- ].filter(Boolean);
4191
- const fallbackSurfaces = surfaces.length > 0 ? surfaces : [
4192
- 'project_roadmap_task',
4193
- 'implementation_acceptance_check',
4194
- 'implementation_evidence',
4195
- 'implementation_gate',
4196
- 'project_close',
4197
- ];
4198
- const closureTaskStatus = itemStatus && !new Set(['done', 'completed', 'complete', 'blocked', 'failed', 'rejected', 'cancelled']).has(itemStatus.toLowerCase())
4199
- ? itemStatus
4200
- : 'in_progress';
4201
- const closureTaskBinding = {
4202
- task_id: implementationItemId || itemKey || resolvedProjectId,
4203
- roadmap_item_id: implementationItemId || itemKey || resolvedProjectId,
4204
- task_status: closureTaskStatus,
4205
- allowed_actions: ['claim_work_item'],
4206
- substrate_action: 'claim_work_item',
4207
- task_type: 'roadmap_closure',
4208
- };
4209
- try {
4210
- freshClaim = await this.startClaimedWork({
4211
- claimName: cleanText(claimName) || `closeRoadmapItemWorkflow:${resolvedProjectId}:${itemKey || implementationItemId}`,
4212
- actorId: normalizedActorId,
4213
- intentId: 'code_mutation',
4214
- declaredScopeFiles: fallbackDeclaredScope,
4215
- allowedMutationSurfaces: fallbackSurfaces,
4216
- runtimeSessionId: resolvedWorkflowRunId || resolvedProjectId,
4217
- executionPurpose: cleanText(executionIntent) || `Close roadmap item ${itemKey || implementationItemId}`,
4218
- successCriteria: 'Active roadmap item closed with verified acceptance checks, artifacts, gate decision, and claim signoff.',
4219
- mutationAllowed: true,
4220
- metadata: {
4221
- source: 'closeRoadmapItemWorkflow',
4222
- project_id: resolvedProjectId,
4223
- workflow_id: resolvedWorkflowId,
4224
- workflow_run_id: resolvedWorkflowRunId,
4225
- task_binding: closureTaskBinding,
4226
- },
4227
- });
4228
- } catch (error) {
4229
- throw new Error(`claim cannot be established: ${error.message}`);
4230
- }
4231
- effectiveClaimId = cleanText(freshClaim?.claim_id) || cleanText(freshClaim?.claim?.claim_id);
4232
- if (!effectiveClaimId) {
4233
- throw new Error('claim cannot be established.');
4234
- }
4235
- }
4236
-
4237
- const acceptanceCheckResult = await this.getImplementationItemAcceptanceChecks(implementationItemId);
4238
- const acceptanceChecks = Array.isArray(acceptanceCheckResult?.acceptance_checks)
4239
- ? acceptanceCheckResult.acceptance_checks.filter((check) => isPlainObject(check))
4240
- : [];
4241
- if (acceptanceChecks.length === 0) {
4242
- throw new Error('acceptance checks are incomplete.');
4243
- }
4244
-
4245
- const normalizedAcceptanceStatus = cleanText(requiredAcceptanceCheckStatus) || 'verified';
4246
- const verifiedAcceptanceChecks = [];
4247
- for (const check of acceptanceChecks) {
4248
- const acceptanceCheckId = cleanText(check.acceptance_check_id) || cleanText(check.acceptanceCheckId);
4249
- if (!acceptanceCheckId) {
4250
- throw new Error('acceptance checks are incomplete.');
4251
- }
4252
- const status = cleanText(check.status).toLowerCase();
4253
- if (status !== normalizedAcceptanceStatus.toLowerCase()) {
4254
- try {
4255
- const verification = await this.updateAcceptanceCheckStatusVerified(
4256
- implementationItemId,
4257
- acceptanceCheckId,
4258
- normalizedAcceptanceStatus,
4259
- {
4260
- updated_by: normalizedActorId,
4261
- workflowId: resolvedWorkflowId,
4262
- claim_id: effectiveClaimId,
4263
- status_reason: 'Roadmap closure workflow verified the required acceptance check.',
4264
- },
4265
- );
4266
- verifiedAcceptanceChecks.push({
4267
- acceptance_check_id: acceptanceCheckId,
4268
- status: normalizedAcceptanceStatus,
4269
- verification,
4270
- });
4271
- } catch (error) {
4272
- throw new Error(`acceptance checks are incomplete: ${error.message}`);
4273
- }
4274
- continue;
4275
- }
4276
- verifiedAcceptanceChecks.push({
4277
- acceptance_check_id: acceptanceCheckId,
4278
- status,
4279
- existing: true,
4280
- });
4281
- }
4282
-
4283
- const artifacts = await this.verifyImplementationItemArtifacts(implementationItemId, {
4284
- requiredArtifacts: cleanList(requiredArtifacts).length > 0
4285
- ? cleanList(requiredArtifacts)
4286
- : ['artifact_manifest', 'decision_packet'],
4287
- });
4288
-
4289
- const evidenceRefs = cleanList(gateEvidenceRefs);
4290
- if (evidenceRefs.length === 0) {
4291
- evidenceRefs.push(closureEvidenceRef || `roadmap-closure:${resolvedProjectId}:${itemKey || implementationItemId}`);
4292
- }
4293
-
4294
- const gateDecisionResult = await this.createImplementationPacketGateDecision(packetId, {
4295
- gate_type: cleanText(gateType) || 'roadmap_closure',
4296
- decision: cleanText(gateDecision) || 'pass',
4297
- rationale: cleanText(gateRationale) || `Roadmap item ${itemKey || implementationItemId} satisfied all closure checks.`,
4298
- evidence_refs: evidenceRefs,
4299
- remediation_actions: cleanList(remediationActions),
4300
- decided_by: normalizedActorId,
4301
- claim_id: effectiveClaimId,
4302
- workflow_id: resolvedWorkflowId,
4303
- workflow_run_id: resolvedWorkflowRunId,
4304
- });
4305
-
4306
- const itemStatusResult = await this.updateImplementationItemStatusVerified(
4307
- implementationItemId,
4308
- cleanText(terminalItemStatus) || 'accepted',
4309
- {
4310
- workflowId: resolvedWorkflowId,
4311
- updated_by: normalizedActorId,
4312
- claim_id: effectiveClaimId,
4313
- status_reason: cleanText(closeProjectReason) || `Roadmap closure workflow advanced ${itemKey || implementationItemId} to terminal status.`,
4314
- },
4315
- );
4316
-
4317
- const closureEvidenceRefValue = cleanText(closureEvidenceRef) || `roadmap-closure:${resolvedProjectId}:${itemKey || implementationItemId}`;
4318
- const closureEvidence = await this.addImplementationItemEvidence(implementationItemId, {
4319
- evidence_type: cleanText(closureEvidenceType) || 'roadmap_closure',
4320
- evidence_ref: closureEvidenceRefValue,
4321
- title: cleanText(closureEvidenceTitle) || `Closure evidence for ${itemKey || implementationItemId}`,
4322
- recorded_by: normalizedActorId,
4323
- metadata: {
4324
- project_id: resolvedProjectId,
4325
- workflow_id: resolvedWorkflowId,
4326
- workflow_run_id: resolvedWorkflowRunId,
4327
- claim_id: effectiveClaimId,
4328
- gate_decision_id: cleanText(gateDecisionResult?.implementation_gate_decision_id) || cleanText(gateDecisionResult?.gate_decision_id),
4329
- gate_type: cleanText(gateType) || 'roadmap_closure',
4330
- },
4331
- });
4332
-
4333
- const signedClaim = await this.signoffClaim(effectiveClaimId, {
4334
- actor_signature: normalizedActorId,
4335
- intent_confirmation: cleanText(executionIntent) || 'I confirm this governed roadmap closure workflow.',
4336
- });
4337
-
4338
- const reloadedActiveItem = await this.getProjectRoadmapActiveItem(resolvedProjectId);
4339
- const remainingOpenTasksPayload = await this.listProjectOpenTasks(resolvedProjectId);
4340
- const remainingOpenTasks = Array.isArray(remainingOpenTasksPayload?.tasks)
4341
- ? remainingOpenTasksPayload.tasks.filter((task) => isPlainObject(task))
4342
- : Array.isArray(remainingOpenTasksPayload)
4343
- ? remainingOpenTasksPayload.filter((task) => isPlainObject(task))
4344
- : [];
4345
- const reloadedItemStatus = cleanText(reloadedActiveItem?.item_status) || cleanText(reloadedActiveItem?.status) || null;
4346
- const terminalStatuses = new Set([
4347
- cleanText(terminalItemStatus) || 'accepted',
4348
- 'accepted',
4349
- 'verified',
4350
- 'done',
4351
- 'completed',
4352
- 'closed',
4353
- ].map((value) => String(value || '').toLowerCase()));
4354
- const activeItemClosed = !reloadedActiveItem || terminalStatuses.has(String(reloadedItemStatus || '').toLowerCase());
4355
- if (!activeItemClosed) {
4356
- throw new Error('project closure state is ambiguous.');
4357
- }
4358
-
4359
- let closeProjectResult = null;
4360
- if (closeProjectIfNoRemainingOpenItems && remainingOpenTasks.length === 0) {
4361
- closeProjectResult = await this.closeActiveProject(resolvedProjectId, {
4362
- workflowId: resolvedWorkflowId,
4363
- workflowRunIds: resolvedWorkflowRunId ? [resolvedWorkflowRunId] : undefined,
4364
- reason: cleanText(closeProjectReason) || `Roadmap item ${itemKey || implementationItemId} closed.`,
4365
- operatorIdentity: normalizedActorId,
4366
- runTerminalStatus: cleanText(terminalItemStatus) || 'accepted',
4367
- });
4368
- }
4369
-
4370
- return {
4371
- status: closeProjectResult ? 'closed' : 'item_closed',
4372
- project_id: resolvedProjectId,
4373
- workflow_id: resolvedWorkflowId,
4374
- workflow_run_id: resolvedWorkflowRunId,
4375
- claim_id: effectiveClaimId,
4376
- claim_source: claimSource,
4377
- continuation_brief: continuation,
4378
- project: projectSummary,
4379
- active_item_before: normalizedActiveItem,
4380
- active_item_after: reloadedActiveItem,
4381
- acceptance_checks: {
4382
- loaded: acceptanceChecks,
4383
- verified: verifiedAcceptanceChecks,
4384
- },
4385
- artifact_verification: artifacts,
4386
- gate_decision: gateDecisionResult,
4387
- implementation_item_status: itemStatusResult,
4388
- closure_evidence: closureEvidence,
4389
- claim_signoff: signedClaim,
4390
- remaining_open_task_count: remainingOpenTasks.length,
4391
- close_project: closeProjectResult,
4392
- };
3977
+ async closeRoadmapItemWorkflow(options = {}) {
3978
+ return this.projects.closeRoadmapItemWorkflow(options);
4393
3979
  }
4394
3980
 
4395
3981
  // LOGA projections are governed runtime documents, not static reports.
@@ -4623,11 +4209,11 @@ export class AIEngineClient {
4623
4209
  }
4624
4210
 
4625
4211
  async getProjectImplementationRoadmapReport(projectId) {
4626
- return this.roadmaps.getProjectImplementationRoadmapReport(projectId);
4212
+ return this.roadmapReports.getProjectImplementationRoadmapReport(projectId);
4627
4213
  }
4628
4214
 
4629
4215
  async downloadProjectImplementationRoadmapReportMarkdown(projectId) {
4630
- return this.roadmaps.downloadProjectImplementationRoadmapReportMarkdown(projectId);
4216
+ return this.roadmapReports.downloadProjectImplementationRoadmapReportMarkdown(projectId);
4631
4217
  }
4632
4218
 
4633
4219
  async ensureProjectRoadmapTaskSurface(projectId, {
@@ -4652,148 +4238,16 @@ export class AIEngineClient {
4652
4238
  notes,
4653
4239
  createAcceptanceSubtasks = true,
4654
4240
  } = {}) {
4655
- if (!isPlainObject(packetPayload)) {
4656
- throw new Error('packetPayload must be a JSON object.');
4657
- }
4658
-
4659
- const normalizedImportedBy = cleanText(importedBy) || cleanText(requestedBy) || this.actorId;
4660
- const normalizedRequestedBy = cleanText(requestedBy) || normalizedImportedBy;
4661
- const normalizedCreatedBy = cleanText(createdBy) || normalizedRequestedBy;
4662
-
4663
- const importedPacket = await this.importImplementationPacket({
4664
- ...packetPayload,
4665
- imported_by: normalizedImportedBy,
4666
- });
4667
- const packetId = cleanText(importedPacket?.packet_id || importedPacket?.packetId || importedPacket?.implementation_packet_key || importedPacket?.implementationPacketKey || packetPayload.packetId || packetPayload.packet_id);
4668
- const implementationPacketId = cleanText(importedPacket?.implementation_packet_id || importedPacket?.implementationPacketId);
4669
- if (!implementationPacketId) {
4670
- throw new Error('Imported implementation packet response must include implementation_packet_id.');
4671
- }
4672
- if (!looksLikeUuid(implementationPacketId)) {
4673
- throw new Error('Imported implementation packet response returned a non-UUID implementation_packet_id.');
4674
- }
4675
- const projectReference = this._resolveImplementationPacketProjectReference(packetPayload);
4676
- if (!projectReference) {
4677
- throw new Error('Could not resolve a project reference from the packet payload.');
4678
- }
4679
- const projectPayload = await this.getProject(projectReference);
4680
- const projectSummary = isPlainObject(projectPayload?.summary) ? projectPayload.summary : {};
4681
- const projectId = cleanText(projectSummary.project_id);
4682
- if (!projectId) {
4683
- throw new Error('Project id could not be resolved.');
4684
- }
4685
-
4686
- const workflowReference = this._resolveImplementationPacketWorkflowReference(packetPayload, {
4687
- projectSummary,
4688
- workflowIdOverride: cleanText(workflowId),
4689
- });
4690
- if (!workflowReference) {
4691
- throw new Error('Could not resolve a workflow reference from the packet payload or project summary.');
4692
- }
4693
- const resolvedWorkflowId = await this._resolveImplementationPacketWorkflowId(workflowReference);
4694
- const workflowSlug = cleanText(projectSummary.workflow_slug);
4695
-
4696
- const binding = await this.bindImplementationPacketToWorkflow(resolvedWorkflowId, {
4697
- packet_id: packetId,
4698
- binding_role: cleanText(bindingRole) || 'active',
4699
- created_by: normalizedCreatedBy,
4700
- notes: cleanText(notes),
4701
- });
4702
-
4703
- const taskSurface = await this.ensureProjectRoadmapTaskSurface(projectId, {
4704
- requestedBy: normalizedRequestedBy,
4241
+ return this.implementationPackets.importImplementationPacketAndMaterializeRoadmap(packetPayload, {
4242
+ importedBy,
4243
+ requestedBy,
4244
+ createdBy,
4245
+ workflowId,
4246
+ bindingRole,
4705
4247
  assignedTo,
4248
+ notes,
4706
4249
  createAcceptanceSubtasks,
4707
4250
  });
4708
-
4709
- const roadmapReport = await this.getProjectImplementationRoadmapReport(projectId);
4710
- const roadmapMarkdown = await this.downloadProjectImplementationRoadmapReportMarkdown(projectId);
4711
- const phases = Array.isArray(packetPayload.phases) ? packetPayload.phases.filter(isPlainObject) : [];
4712
- const roadmapItems = phases.flatMap((phase) => (
4713
- Array.isArray(phase.items) ? phase.items.filter(isPlainObject) : []
4714
- ));
4715
- const taskRows = Array.isArray(taskSurface?.task_surfaces) ? taskSurface.task_surfaces.filter(isPlainObject) : [];
4716
- const subtaskRows = taskRows.filter((task) => cleanText(task.parent_task_id) !== null);
4717
- const acceptanceCheckCount = roadmapItems.reduce(
4718
- (total, item) => total + (Array.isArray(item.acceptanceChecks) ? item.acceptanceChecks.filter(Boolean).length : 0),
4719
- 0,
4720
- );
4721
-
4722
- return {
4723
- status: 'materialized',
4724
- project_id: projectId,
4725
- project_slug: cleanText(projectSummary.project_slug),
4726
- workflow_id: resolvedWorkflowId,
4727
- workflow_slug: workflowSlug,
4728
- implementation_packet_id: implementationPacketId,
4729
- implementation_packet_key: packetId,
4730
- binding,
4731
- task_surface: taskSurface,
4732
- roadmap_projection: {
4733
- report: roadmapReport,
4734
- markdown: roadmapMarkdown,
4735
- count_lines: countRoadmapProjectionLines(roadmapMarkdown),
4736
- phase_count: phases.length,
4737
- roadmap_item_count: roadmapItems.length,
4738
- task_count: taskRows.length,
4739
- subtask_count: subtaskRows.length,
4740
- acceptance_check_count: acceptanceCheckCount,
4741
- },
4742
- };
4743
- }
4744
-
4745
- async listProjectOpenTasks(projectId) {
4746
- return this.projects.listProjectOpenTasks(projectId);
4747
- }
4748
-
4749
- async getProjectPerformanceMetrics(projectId, { workflowId, workflowRunId, sinceUtc } = {}) {
4750
- return this.projects.getProjectPerformanceMetrics(projectId, { workflowId, workflowRunId, sinceUtc });
4751
- }
4752
-
4753
- _resolveImplementationPacketProjectReference(packetPayload) {
4754
- return cleanText(packetPayload.projectId)
4755
- || cleanText(packetPayload.project_id)
4756
- || cleanText(packetPayload.projectSlug)
4757
- || cleanText(packetPayload.project_slug)
4758
- || cleanText(packetPayload?.scope?.projectId)
4759
- || cleanText(packetPayload?.scope?.project_id)
4760
- || cleanText(packetPayload?.scope?.projectSlug)
4761
- || cleanText(packetPayload?.scope?.project_slug)
4762
- || cleanText(packetPayload?.system?.slug);
4763
- }
4764
-
4765
- _resolveImplementationPacketWorkflowReference(packetPayload, { projectSummary, workflowIdOverride } = {}) {
4766
- if (workflowIdOverride) return workflowIdOverride;
4767
- return cleanText(packetPayload.workflowId)
4768
- || cleanText(packetPayload.workflow_id)
4769
- || cleanText(packetPayload.workflowSlug)
4770
- || cleanText(packetPayload.workflow_slug)
4771
- || cleanText(packetPayload?.scope?.workflowId)
4772
- || cleanText(packetPayload?.scope?.workflow_id)
4773
- || cleanText(packetPayload?.scope?.workflowSlug)
4774
- || cleanText(packetPayload?.scope?.workflow_slug)
4775
- || cleanText(projectSummary?.workflow_id)
4776
- || cleanText(projectSummary?.workflow_slug);
4777
- }
4778
-
4779
- async _resolveImplementationPacketWorkflowId(workflowReference) {
4780
- const normalized = cleanText(workflowReference);
4781
- if (!normalized) {
4782
- throw new Error('workflow reference is required.');
4783
- }
4784
- const workflow = await this.getWorkflow(normalized);
4785
- if (workflow) {
4786
- return cleanText(workflow.workflow_id) || cleanText(workflow.workflowId) || normalized;
4787
- }
4788
- const workflowList = await this.listWorkflows();
4789
- const workflows = Array.isArray(workflowList) ? workflowList : workflowList?.workflows;
4790
- const matches = Array.isArray(workflows)
4791
- ? workflows.filter((item) => cleanText(item?.slug) === normalized)
4792
- : [];
4793
- if (matches.length === 1) {
4794
- return cleanText(matches[0].workflow_id) || cleanText(matches[0].workflowId) || normalized;
4795
- }
4796
- throw new Error(`Workflow not found: ${normalized}`);
4797
4251
  }
4798
4252
 
4799
4253
  // ─── Implementation Tasks ──────────────────────────────────────────────────
@@ -4822,7 +4276,7 @@ export class AIEngineClient {
4822
4276
  executionType,
4823
4277
  executionPurpose,
4824
4278
  } = {}) {
4825
- return this.implementationItems.createImplementationTask(implementationItemId, {
4279
+ return this.implementationTasks.createImplementationTask(implementationItemId, {
4826
4280
  title,
4827
4281
  implementationPacketId,
4828
4282
  description,
@@ -4849,23 +4303,23 @@ export class AIEngineClient {
4849
4303
  }
4850
4304
 
4851
4305
  async listImplementationTasks(implementationItemId) {
4852
- return this.implementationItems.listImplementationTasks(implementationItemId);
4306
+ return this.implementationTasks.listImplementationTasks(implementationItemId);
4853
4307
  }
4854
4308
 
4855
4309
  async listImplementationSubtasks(taskId) {
4856
- return this.implementationItems.listImplementationSubtasks(taskId);
4310
+ return this.implementationTasks.listImplementationSubtasks(taskId);
4857
4311
  }
4858
4312
 
4859
4313
  async updateImplementationTask(taskId, updates) {
4860
- return this.implementationItems.updateImplementationTask(taskId, updates);
4314
+ return this.implementationTasks.updateImplementationTask(taskId, updates);
4861
4315
  }
4862
4316
 
4863
4317
  async assignImplementationTask(taskId, { assignedTo, assignedBy } = {}) {
4864
- return this.implementationItems.assignImplementationTask(taskId, { assignedTo, assignedBy });
4318
+ return this.implementationTasks.assignImplementationTask(taskId, { assignedTo, assignedBy });
4865
4319
  }
4866
4320
 
4867
4321
  async completeImplementationTask(taskId, { completedBy } = {}) {
4868
- return this.implementationItems.completeImplementationTask(taskId, { completedBy });
4322
+ return this.implementationTasks.completeImplementationTask(taskId, { completedBy });
4869
4323
  }
4870
4324
 
4871
4325
  // ─── Governed Implementation ───────────────────────────────────────────────
@@ -4883,7 +4337,7 @@ export class AIEngineClient {
4883
4337
  }
4884
4338
 
4885
4339
  async getImplementationItemAcceptanceChecks(implementationItemId) {
4886
- return this.implementationGates.getImplementationItemAcceptanceChecks(implementationItemId);
4340
+ return this.implementationChecks.getImplementationItemAcceptanceChecks(implementationItemId);
4887
4341
  }
4888
4342
 
4889
4343
  async getArtifactManifest(implementationItemId) {
@@ -4895,46 +4349,7 @@ export class AIEngineClient {
4895
4349
  }
4896
4350
 
4897
4351
  async updateImplementationItemStatus(implementationItemId, body) {
4898
- await this._assertExecutionEligibility({
4899
- ...(body || {}),
4900
- claimed_item_id: body?.claimed_item_id || implementationItemId,
4901
- requested_mutation_surface: body?.requested_mutation_surface || 'implementation_packet_item',
4902
- verification_required: true,
4903
- });
4904
- const result = await this._request(`/api/governed-implementation/items/${implementationItemId}/status`, {
4905
- method: 'PATCH', body,
4906
- });
4907
- const expectedStatus = cleanText(body?.status);
4908
- const authoritativeItem = isPlainObject(result?.implementation_item) ? result.implementation_item : null;
4909
- const actualStatus = cleanText(authoritativeItem?.status);
4910
- if (expectedStatus && !authoritativeItem) {
4911
- throw buildVerificationError(
4912
- `updateImplementationItemStatus did not return authoritative item state for ${implementationItemId}.`,
4913
- {
4914
- mutation_name: 'updateImplementationItemStatus',
4915
- mutation_attempted: true,
4916
- authoritative_read_performed: false,
4917
- post_condition_verified: false,
4918
- expected_state: { status: expectedStatus },
4919
- mutation_result: result ?? null,
4920
- },
4921
- );
4922
- }
4923
- if (expectedStatus && actualStatus !== expectedStatus) {
4924
- throw buildVerificationError(
4925
- `updateImplementationItemStatus returned ${actualStatus ?? 'unknown'} instead of ${expectedStatus}.`,
4926
- {
4927
- mutation_name: 'updateImplementationItemStatus',
4928
- mutation_attempted: true,
4929
- authoritative_read_performed: true,
4930
- post_condition_verified: false,
4931
- expected_state: { status: expectedStatus },
4932
- verified_current_state: authoritativeItem ?? result ?? null,
4933
- mutation_result: result ?? null,
4934
- },
4935
- );
4936
- }
4937
- return result;
4352
+ return this.implementationItems.updateImplementationItemStatus(implementationItemId, body);
4938
4353
  }
4939
4354
 
4940
4355
  async addImplementationItemEvidence(implementationItemId, body) {
@@ -4950,16 +4365,7 @@ export class AIEngineClient {
4950
4365
  }
4951
4366
 
4952
4367
  async updateAcceptanceCheckStatus(implementationItemId, acceptanceCheckId, body) {
4953
- await this._assertExecutionEligibility({
4954
- ...(body || {}),
4955
- claimed_item_id: body?.claimed_item_id || implementationItemId,
4956
- requested_mutation_surface: body?.requested_mutation_surface || 'implementation_acceptance_check',
4957
- verification_required: true,
4958
- });
4959
- return this._request(
4960
- `/api/governed-implementation/items/${implementationItemId}/acceptance-checks/${acceptanceCheckId}/status`,
4961
- { method: 'PATCH', body }
4962
- );
4368
+ return this.implementationChecks.updateAcceptanceCheckStatus(implementationItemId, acceptanceCheckId, body);
4963
4369
  }
4964
4370
 
4965
4371
  async createImplementationPacketGateDecision(packetId, body) {
@@ -4985,223 +4391,25 @@ export class AIEngineClient {
4985
4391
  expectedState,
4986
4392
  evidenceLabel,
4987
4393
  } = {}) {
4988
- if (typeof mutationFn !== 'function') throw new Error('mutationFn is required.');
4989
- if (typeof verificationFn !== 'function') throw new Error('verificationFn is required.');
4990
- const normalizedMutationName = cleanText(mutationName) || 'verifiedMutation';
4991
- const normalizedEvidenceLabel = cleanText(evidenceLabel) || normalizedMutationName;
4992
-
4993
- let mutationResult;
4994
- try {
4995
- mutationResult = await mutationFn();
4996
- } catch (error) {
4997
- throw buildVerificationError(
4998
- `${normalizedMutationName} mutation failed before post-condition verification.`,
4999
- {
5000
- mutation_name: normalizedMutationName,
5001
- evidence_label: normalizedEvidenceLabel,
5002
- mutation_attempted: true,
5003
- authoritative_read_performed: false,
5004
- post_condition_verified: false,
5005
- expected_state: expectedState ?? null,
5006
- mutation_error: {
5007
- message: error?.message || String(error),
5008
- status: error?.status || null,
5009
- payload: error?.payload || null,
5010
- },
5011
- },
5012
- );
5013
- }
5014
-
5015
- let verificationResult;
5016
- try {
5017
- verificationResult = await verificationFn(mutationResult);
5018
- } catch (error) {
5019
- throw buildVerificationError(
5020
- `${normalizedMutationName} authoritative read failed after mutation attempt.`,
5021
- {
5022
- mutation_name: normalizedMutationName,
5023
- evidence_label: normalizedEvidenceLabel,
5024
- mutation_attempted: true,
5025
- authoritative_read_performed: false,
5026
- post_condition_verified: false,
5027
- expected_state: expectedState ?? null,
5028
- mutation_result: mutationResult ?? null,
5029
- verification_error: {
5030
- message: error?.message || String(error),
5031
- status: error?.status || null,
5032
- payload: error?.payload || null,
5033
- },
5034
- },
5035
- );
5036
- }
5037
-
5038
- let evaluation;
5039
- if (typeof expectedState === 'function') {
5040
- evaluation = expectedState(verificationResult, mutationResult);
5041
- } else {
5042
- evaluation = {
5043
- ok: matchesExpectedState(verificationResult, expectedState),
5044
- verifiedCurrentState: verificationResult,
5045
- };
5046
- }
5047
- if (typeof evaluation === 'boolean') {
5048
- evaluation = { ok: evaluation, verifiedCurrentState: verificationResult };
5049
- }
5050
- if (!isPlainObject(evaluation) || typeof evaluation.ok !== 'boolean') {
5051
- throw new Error('expectedState must resolve to a boolean or an object with an ok field.');
5052
- }
5053
-
5054
- const evidence = {
5055
- mutation_name: normalizedMutationName,
5056
- evidence_label: normalizedEvidenceLabel,
5057
- mutation_attempted: true,
5058
- authoritative_read_performed: true,
5059
- post_condition_verified: evaluation.ok,
5060
- expected_state: expectedState ?? null,
5061
- verified_current_state: evaluation.verifiedCurrentState ?? verificationResult ?? null,
5062
- mutation_result: mutationResult ?? null,
5063
- verification_result: verificationResult ?? null,
5064
- verification_message: cleanText(evaluation.message) || null,
5065
- };
5066
-
5067
- if (!evaluation.ok) {
5068
- throw buildVerificationError(
5069
- cleanText(evaluation.message) || `${normalizedMutationName} post-condition verification failed.`,
5070
- evidence,
5071
- );
5072
- }
5073
-
5074
- return {
5075
- mutationName: normalizedMutationName,
5076
- evidenceLabel: normalizedEvidenceLabel,
5077
- mutationResult,
5078
- verificationResult,
5079
- verifiedCurrentState: evidence.verified_current_state,
5080
- mutationVerificationEvidence: evidence,
5081
- };
4394
+ return executeVerifiedMutation({
4395
+ mutationName,
4396
+ mutationFn,
4397
+ verificationFn,
4398
+ expectedState,
4399
+ evidenceLabel,
4400
+ });
5082
4401
  }
5083
4402
 
5084
4403
  async updateImplementationItemStatusVerified(implementationItemId, status, body = {}) {
5085
- return this.executeVerifiedMutation({
5086
- mutationName: 'updateImplementationItemStatus',
5087
- evidenceLabel: `implementation-item-status:${implementationItemId}`,
5088
- mutationFn: () => this.updateImplementationItemStatus(implementationItemId, { ...body, status }),
5089
- verificationFn: async (mutationResult) => {
5090
- const workflowId = cleanText(body.workflowId)
5091
- || cleanText(body.workflow_id)
5092
- || cleanText(mutationResult?.workflow_id);
5093
- if (!workflowId) {
5094
- throw new Error('workflowId is required to verify implementation item status.');
5095
- }
5096
- const roadmap = await this.getWorkflowImplementationRoadmap(workflowId);
5097
- const items = Array.isArray(roadmap?.items) ? roadmap.items : [];
5098
- const matchedItem = items.find((item) => item?.implementation_item_id === implementationItemId) || null;
5099
- return {
5100
- workflow_id: workflowId,
5101
- item: matchedItem,
5102
- roadmap,
5103
- };
5104
- },
5105
- expectedState: (verificationResult) => {
5106
- const item = verificationResult?.item || null;
5107
- if (!item) {
5108
- return {
5109
- ok: false,
5110
- message: `Implementation item ${implementationItemId} was not present in the authoritative roadmap read.`,
5111
- verifiedCurrentState: verificationResult,
5112
- };
5113
- }
5114
- return {
5115
- ok: item.status === status,
5116
- message: `Implementation item ${implementationItemId} status remained ${item.status ?? 'unknown'} after mutation attempt.`,
5117
- verifiedCurrentState: item,
5118
- };
5119
- },
5120
- });
4404
+ return this.implementationItems.updateImplementationItemStatusVerified(implementationItemId, status, body);
5121
4405
  }
5122
4406
 
5123
4407
  async updateAcceptanceCheckStatusVerified(implementationItemId, acceptanceCheckId, status, body = {}) {
5124
- return this.executeVerifiedMutation({
5125
- mutationName: 'updateAcceptanceCheckStatus',
5126
- evidenceLabel: `acceptance-check-status:${implementationItemId}:${acceptanceCheckId}`,
5127
- mutationFn: () => this.updateAcceptanceCheckStatus(implementationItemId, acceptanceCheckId, { ...body, status }),
5128
- verificationFn: async () => this.getImplementationItemAcceptanceChecks(implementationItemId),
5129
- expectedState: (verificationResult) => {
5130
- const checks = Array.isArray(verificationResult?.acceptance_checks) ? verificationResult.acceptance_checks : [];
5131
- const targetCheck = checks.find((check) => check?.acceptance_check_id === acceptanceCheckId) || null;
5132
- if (!targetCheck) {
5133
- return {
5134
- ok: false,
5135
- message: `Acceptance check ${acceptanceCheckId} was not returned by the authoritative read.`,
5136
- verifiedCurrentState: verificationResult,
5137
- };
5138
- }
5139
- return {
5140
- ok: targetCheck.status === status,
5141
- message: `Acceptance check ${acceptanceCheckId} status remained ${targetCheck.status ?? 'unknown'} after mutation attempt.`,
5142
- verifiedCurrentState: targetCheck,
5143
- };
5144
- },
5145
- });
4408
+ return this.implementationChecks.updateAcceptanceCheckStatusVerified(implementationItemId, acceptanceCheckId, status, body);
5146
4409
  }
5147
4410
 
5148
4411
  async verifyImplementationItemArtifacts(implementationItemId, { requiredArtifacts = [] } = {}) {
5149
- const requiredKinds = Array.isArray(requiredArtifacts) ? requiredArtifacts : [];
5150
- const verifications = [];
5151
- const artifactReaders = {
5152
- artifact_manifest: async () => this.getArtifactManifest(implementationItemId),
5153
- decision_packet: async () => this.getDecisionPacket(implementationItemId),
5154
- };
5155
-
5156
- for (const kind of requiredKinds) {
5157
- const reader = artifactReaders[kind];
5158
- if (typeof reader !== 'function') {
5159
- throw new Error(`Unsupported required artifact: ${kind}`);
5160
- }
5161
- const payload = await reader();
5162
- if (payload?.source_truth !== 'sql') {
5163
- throw buildVerificationError(`Artifact ${kind} did not declare SQL as source_truth.`, {
5164
- artifact_kind: kind,
5165
- verified_current_state: payload ?? null,
5166
- });
5167
- }
5168
- if (payload?.item_id !== implementationItemId) {
5169
- throw buildVerificationError(`Artifact ${kind} returned item_id ${payload?.item_id ?? 'unknown'} instead of ${implementationItemId}.`, {
5170
- artifact_kind: kind,
5171
- verified_current_state: payload ?? null,
5172
- });
5173
- }
5174
- const dataKey = kind === 'artifact_manifest' ? 'artifact_manifest' : 'decision_packet';
5175
- if (!isPlainObject(payload?.[dataKey])) {
5176
- throw buildVerificationError(`Artifact ${kind} did not include required ${dataKey} data.`, {
5177
- artifact_kind: kind,
5178
- verified_current_state: payload ?? null,
5179
- });
5180
- }
5181
- verifications.push({
5182
- artifact_kind: kind,
5183
- http_status: 200,
5184
- source_truth: payload.source_truth,
5185
- item_id: payload.item_id,
5186
- verified_current_state: payload[dataKey],
5187
- });
5188
- }
5189
-
5190
- return {
5191
- itemId: implementationItemId,
5192
- requiredArtifacts: requiredKinds,
5193
- verified: true,
5194
- mutationVerificationEvidence: {
5195
- mutation_name: 'verifyImplementationItemArtifacts',
5196
- evidence_label: `implementation-item-artifacts:${implementationItemId}`,
5197
- mutation_attempted: false,
5198
- authoritative_read_performed: true,
5199
- post_condition_verified: true,
5200
- expected_state: { required_artifacts: requiredKinds },
5201
- verified_current_state: verifications,
5202
- },
5203
- artifacts: verifications,
5204
- };
4412
+ return this.implementationArtifacts.verifyImplementationItemArtifacts(implementationItemId, { requiredArtifacts });
5205
4413
  }
5206
4414
 
5207
4415
  // ─── Skills ────────────────────────────────────────────────────────────────
@@ -5437,3495 +4645,41 @@ export class AIEngineClient {
5437
4645
  includeLogaPortfolioProjection = false,
5438
4646
  includeLogaRoadmapProjections = false,
5439
4647
  } = {}) {
5440
- const portfolioBundle = await this.getPortfolioBundle();
5441
- const projectListPayload = await this.listProjects({
5442
- limit: projectLimit,
4648
+ return this.portfolio.getPortfolioClosureReadiness({
4649
+ projectLimit,
5443
4650
  includeInactive,
4651
+ includeLogaPortfolioProjection,
4652
+ includeLogaRoadmapProjections,
5444
4653
  });
4654
+ }
5445
4655
 
5446
- const bundleProjects = Array.isArray(portfolioBundle?.projects)
5447
- ? portfolioBundle.projects.filter((project) => isPlainObject(project))
5448
- : [];
5449
- const listedProjects = Array.isArray(projectListPayload?.projects)
5450
- ? projectListPayload.projects.filter((project) => isPlainObject(project))
5451
- : Array.isArray(projectListPayload)
5452
- ? projectListPayload.filter((project) => isPlainObject(project))
5453
- : [];
5454
- const activeProjects = listedProjects.length > 0 ? listedProjects : bundleProjects;
5455
- const logaPortfolioProjection = includeLogaPortfolioProjection
5456
- ? await this.getLogaProjectPortfolioProjection()
5457
- : null;
5458
- const logaRoadmapProjections = {};
4656
+ async registerModernizationAsset(request = {}) {
4657
+ return this.workflowComposition.registerModernizationAsset(request);
4658
+ }
5459
4659
 
5460
- const projectReadiness = [];
5461
- for (const project of activeProjects) {
5462
- const projectId = cleanText(project.project_id) || cleanText(project.projectId);
5463
- if (!projectId) {
5464
- continue;
5465
- }
4660
+ async classifyModernizationAsset(request = {}) {
4661
+ return this.workflowComposition.classifyModernizationAsset(request);
4662
+ }
5466
4663
 
5467
- const roadmapSummaryPayload = await this.getProjectRoadmapSummary(projectId);
5468
- const roadmapSummary = isPlainObject(roadmapSummaryPayload?.summary) ? roadmapSummaryPayload.summary : {};
5469
- const activeItemPayload = await this.getProjectRoadmapActiveItem(projectId);
5470
- const activeItem = isPlainObject(activeItemPayload?.active_item)
5471
- ? activeItemPayload.active_item
5472
- : isPlainObject(activeItemPayload)
5473
- ? activeItemPayload
5474
- : {};
5475
- const openTasksPayload = await this.listProjectOpenTasks(projectId);
5476
- const openTasks = Array.isArray(openTasksPayload?.tasks)
5477
- ? openTasksPayload.tasks.filter((task) => isPlainObject(task))
5478
- : Array.isArray(openTasksPayload)
5479
- ? openTasksPayload.filter((task) => isPlainObject(task))
5480
- : [];
5481
-
5482
- const activeItemStatus = cleanText(activeItem.item_status) || cleanText(activeItem.status);
5483
- const completionPercentage = Number(
5484
- roadmapSummary.completion_percentage
5485
- ?? roadmapSummary.completion_pct
5486
- ?? roadmapSummary.progress_percentage
5487
- ?? 0,
5488
- );
5489
- const totalItems = Number(
5490
- roadmapSummary.total_items
5491
- ?? roadmapSummary.item_count
5492
- ?? roadmapSummary.total_count
5493
- ?? 0,
5494
- );
5495
- const openItems = Number(
5496
- roadmapSummary.open_items
5497
- ?? roadmapSummary.open_item_count
5498
- ?? roadmapSummary.remaining_items
5499
- ?? openTasks.length,
5500
- );
5501
- const terminalStatuses = new Set(['accepted', 'verified', 'done', 'completed', 'closed']);
5502
- const activeItemReady = !activeItemStatus || terminalStatuses.has(activeItemStatus.toLowerCase());
5503
- const projectReady = openItems === 0 && openTasks.length === 0 && activeItemReady && completionPercentage >= 100;
5504
- const blockingReason = projectReady
5505
- ? null
5506
- : [
5507
- openTasks.length > 0 ? `${openTasks.length} open task(s) remain` : null,
5508
- openItems > 0 ? `${openItems} roadmap item(s) remain open` : null,
5509
- !activeItemReady ? `active item status is ${activeItemStatus || 'missing'}` : null,
5510
- completionPercentage < 100 ? `completion is ${completionPercentage.toFixed(1)}%` : null,
5511
- ].filter(Boolean).join('; ') || 'portfolio closure readiness is not satisfied';
5512
-
5513
- const roadmapCompletion = {
5514
- completion_percentage: completionPercentage,
5515
- total_items: totalItems,
5516
- open_items: openItems,
5517
- completed_items: Math.max(totalItems - openItems, 0),
5518
- };
5519
- const projectReadinessEntry = {
5520
- project: {
5521
- project_id: projectId,
5522
- project_name: cleanText(project.project_name) || cleanText(project.projectName) || null,
5523
- project_slug: cleanText(project.project_slug) || cleanText(project.projectSlug) || null,
5524
- process_status: cleanText(project.process_status) || cleanText(project.processStatus) || null,
5525
- charter_status: cleanText(project.charter_status) || cleanText(project.charterStatus) || null,
5526
- },
5527
- roadmap_completion: roadmapCompletion,
5528
- active_item: activeItem,
5529
- open_task_count: openTasks.length,
5530
- closure_ready: projectReady,
5531
- blocking_reason: blockingReason,
5532
- };
5533
- if (includeLogaRoadmapProjections) {
5534
- projectReadinessEntry.loga_roadmap_projection = await this.getLogaProjectRoadmapProjection(projectId);
5535
- logaRoadmapProjections[projectId] = projectReadinessEntry.loga_roadmap_projection;
5536
- }
5537
- projectReadiness.push(projectReadinessEntry);
5538
- }
4664
+ async discoverSalvageCandidates(request = {}) {
4665
+ return this.workflowComposition.discoverSalvageCandidates(request);
4666
+ }
5539
4667
 
5540
- const closureReady = projectReadiness.length === 0 || projectReadiness.every((project) => project.closure_ready === true);
5541
- const blockingProject = projectReadiness.find((project) => project.closure_ready !== true);
4668
+ async createModernizationWorkPacket(request = {}) {
4669
+ return this.workflowComposition.createModernizationWorkPacket(request);
4670
+ }
5542
4671
 
5543
- return {
5544
- portfolio_summary: isPlainObject(portfolioBundle?.summary) ? portfolioBundle.summary : {},
5545
- portfolio_bundle: portfolioBundle,
5546
- active_projects: projectReadiness,
5547
- closure_readiness: closureReady,
5548
- blocking_reason: closureReady ? null : `${cleanText(blockingProject?.project?.project_name) || cleanText(blockingProject?.project?.project_id) || 'project'}: ${blockingProject?.blocking_reason || 'closure readiness is not satisfied'}`,
5549
- blocking_project_id: closureReady ? null : cleanText(blockingProject?.project?.project_id),
5550
- blocking_project_name: closureReady ? null : cleanText(blockingProject?.project?.project_name),
5551
- project_count: projectReadiness.length,
5552
- open_task_count: projectReadiness.reduce((total, project) => total + Number(project.open_task_count || 0), 0),
5553
- loga_portfolio_projection: logaPortfolioProjection,
5554
- loga_roadmap_projections: includeLogaRoadmapProjections ? logaRoadmapProjections : null,
5555
- };
4672
+ async requestModernizationWrapperExecution(request = {}) {
4673
+ return this.workflowComposition.requestModernizationWrapperExecution(request);
5556
4674
  }
5557
4675
 
5558
- async registerModernizationAsset({
5559
- projectIdentifier,
5560
- projectId,
5561
- relatedProjectId,
5562
- related_project_id,
5563
- startWorkBody = {},
5564
- startWorkRequest = {},
5565
- resumeProjectWorkOptions = {},
5566
- asset = {},
5567
- modernizationAsset = {},
5568
- assetName,
5569
- asset_name,
5570
- assetType,
5571
- asset_type,
5572
- sourceRef,
5573
- source_ref,
5574
- originSystem,
5575
- origin_system,
5576
- businessContext,
5577
- business_context,
5578
- suspectedValue,
5579
- suspected_value,
5580
- knownRisks = [],
5581
- known_risks = [],
5582
- evidenceRefs = [],
5583
- evidence_refs = [],
5584
- handoffNotes,
5585
- handoff_notes,
5586
- assignedClassifier,
5587
- assigned_classifier,
5588
- classificationRequired,
5589
- classification_required,
5590
- submittedBy,
5591
- submitted_by,
5592
- includePortfolioBundle = true,
5593
- includeProjectRoadmap = true,
5594
- includeProjectActiveItem = true,
5595
- metadata = {},
5596
- } = {}) {
5597
- const missingSurfaces = [];
5598
- const normalizedMetadata = isPlainObject(metadata) ? metadata : {};
5599
- const normalizedAsset = isPlainObject(modernizationAsset)
5600
- ? modernizationAsset
5601
- : (isPlainObject(asset) ? asset : {});
5602
- const normalizedProjectReference = cleanText(projectIdentifier)
5603
- || cleanText(projectId)
5604
- || cleanText(relatedProjectId)
5605
- || cleanText(related_project_id)
5606
- || cleanText(normalizedAsset.related_project_id)
5607
- || cleanText(normalizedAsset.relatedProjectId);
5608
- const normalizedAssetName = cleanText(asset_name) || cleanText(assetName) || cleanText(normalizedAsset.asset_name) || cleanText(normalizedAsset.assetName);
5609
- const normalizedAssetType = cleanText(asset_type) || cleanText(assetType) || cleanText(normalizedAsset.asset_type) || cleanText(normalizedAsset.assetType) || 'unknown';
5610
- const normalizedSourceRef = cleanText(source_ref) || cleanText(sourceRef) || cleanText(normalizedAsset.source_ref) || cleanText(normalizedAsset.sourceRef) || normalizedAssetName;
5611
- if (!normalizedAssetName && !normalizedSourceRef) {
5612
- throw new Error('asset_name or source_ref is required.');
5613
- }
5614
- const normalizedOriginSystem = cleanText(origin_system) || cleanText(originSystem) || cleanText(normalizedAsset.origin_system) || cleanText(normalizedAsset.originSystem);
5615
- const normalizedBusinessContext = cleanText(business_context) || cleanText(businessContext) || cleanText(normalizedAsset.business_context) || cleanText(normalizedAsset.businessContext);
5616
- const normalizedSuspectedValue = cleanText(suspected_value) || cleanText(suspectedValue) || cleanText(normalizedAsset.suspected_value) || cleanText(normalizedAsset.suspectedValue);
5617
- const normalizedKnownRisks = Array.isArray(known_risks)
5618
- ? known_risks
5619
- : (Array.isArray(knownRisks) ? knownRisks : normalizedAsset.known_risks || normalizedAsset.knownRisks || []);
5620
- const normalizedEvidenceRefs = Array.isArray(evidence_refs)
5621
- ? evidence_refs
5622
- : (Array.isArray(evidenceRefs) ? evidenceRefs : normalizedAsset.evidence_refs || normalizedAsset.evidenceRefs || []);
5623
- const normalizedHandoffNotes = cleanText(handoff_notes) || cleanText(handoffNotes) || cleanText(normalizedAsset.handoff_notes) || cleanText(normalizedAsset.handoffNotes);
5624
- const normalizedAssignedClassifier = cleanText(assigned_classifier)
5625
- || cleanText(assignedClassifier)
5626
- || cleanText(normalizedAsset.assigned_classifier)
5627
- || cleanText(normalizedAsset.assignedClassifier);
5628
- const normalizedSubmittedBy = cleanText(submitted_by)
5629
- || cleanText(submittedBy)
5630
- || this.agentSessionId
5631
- || this.actorId
5632
- || null;
5633
- const normalizedClassificationRequired = classification_required ?? classificationRequired ?? true;
4676
+ async getModernizationWrapperEvidence(request = {}) {
4677
+ return this.workflowComposition.getModernizationWrapperEvidence(request);
4678
+ }
5634
4679
 
5635
- let continuation = null;
5636
- let startWorkResult = null;
5637
- if (normalizedProjectReference) {
5638
- if (typeof this.resumeProjectWork === 'function') {
5639
- continuation = await this.resumeProjectWork({
5640
- projectIdentifier: normalizedProjectReference,
5641
- projectId: normalizedProjectReference,
5642
- requireClaim: false,
5643
- ...resumeProjectWorkOptions,
5644
- });
5645
- } else {
5646
- missingSurfaces.push('resumeProjectWork');
5647
- }
5648
- } else if (Object.keys(isPlainObject(startWorkRequest) ? startWorkRequest : {}).length > 0 || Object.keys(isPlainObject(startWorkBody) ? startWorkBody : {}).length > 0) {
5649
- if (typeof this.startWork === 'function') {
5650
- startWorkResult = await this.startWork({
5651
- ...(isPlainObject(startWorkBody) ? startWorkBody : {}),
5652
- ...(isPlainObject(startWorkRequest) ? startWorkRequest : {}),
5653
- });
5654
- } else {
5655
- missingSurfaces.push('startWork');
5656
- }
5657
- } else {
5658
- throw new Error('projectIdentifier is required.');
5659
- }
5660
-
5661
- const projectPayload = isPlainObject(continuation?.project)
5662
- ? continuation.project
5663
- : isPlainObject(startWorkResult?.project)
5664
- ? startWorkResult.project
5665
- : isPlainObject(continuation?.summary)
5666
- ? continuation.summary
5667
- : {};
5668
- const resolvedProjectId = cleanText(projectPayload.project_id)
5669
- || cleanText(projectPayload.projectId)
5670
- || normalizedProjectReference;
5671
- const resolvedWorkflowId = cleanText(projectPayload.workflow_id)
5672
- || cleanText(projectPayload.workflowId)
5673
- || cleanText(continuation?.workflow_id)
5674
- || cleanText(startWorkResult?.workflow_id);
5675
- const resolvedWorkflowRunId = cleanText(projectPayload.workflow_run_id)
5676
- || cleanText(projectPayload.workflowRunId)
5677
- || cleanText(continuation?.workflow_run_id)
5678
- || cleanText(startWorkResult?.workflow_run_id)
5679
- || cleanText(startWorkResult?.workflowRunId);
5680
-
5681
- const portfolioBundle = includePortfolioBundle && typeof this.getPortfolioBundle === 'function'
5682
- ? await this.getPortfolioBundle().catch(() => null)
5683
- : null;
5684
- const projectRoadmapSummary = resolvedProjectId && includeProjectRoadmap && typeof this.getProjectRoadmapSummary === 'function'
5685
- ? await this.getProjectRoadmapSummary(resolvedProjectId).catch(() => null)
5686
- : null;
5687
- const projectActiveItem = resolvedProjectId && includeProjectActiveItem && typeof this.getProjectRoadmapActiveItem === 'function'
5688
- ? await this.getProjectRoadmapActiveItem(resolvedProjectId).catch(() => null)
5689
- : null;
5690
-
5691
- let assetIntakeRecord = null;
5692
- const assetIntakeSurface = typeof this.createModernizationAssetIntakeRecord === 'function'
5693
- ? this.createModernizationAssetIntakeRecord.bind(this)
5694
- : null;
5695
- if (!assetIntakeSurface) {
5696
- missingSurfaces.push('createModernizationAssetIntakeRecord');
5697
- } else {
5698
- assetIntakeRecord = await assetIntakeSurface({
5699
- projectId: resolvedProjectId,
5700
- workflowId: resolvedWorkflowId,
5701
- workflowRunId: resolvedWorkflowRunId,
5702
- assetName: normalizedAssetName,
5703
- assetType: normalizedAssetType,
5704
- sourceRef: normalizedSourceRef,
5705
- originSystem: normalizedOriginSystem,
5706
- businessContext: normalizedBusinessContext,
5707
- suspectedValue: normalizedSuspectedValue,
5708
- knownRisks: normalizedKnownRisks,
5709
- relatedProjectId: resolvedProjectId,
5710
- evidenceRefs: normalizedEvidenceRefs,
5711
- handoffNotes: normalizedHandoffNotes,
5712
- submittedBy: normalizedSubmittedBy,
5713
- metadata: {
5714
- ...normalizedMetadata,
5715
- source: 'registerModernizationAsset',
5716
- },
5717
- });
5718
- }
5719
-
5720
- let intakeObservation = null;
5721
- const observationSurface = typeof this.submitModernizationAssetObservation === 'function'
5722
- ? this.submitModernizationAssetObservation.bind(this)
5723
- : null;
5724
- if (!observationSurface) {
5725
- missingSurfaces.push('submitModernizationAssetObservation');
5726
- } else {
5727
- intakeObservation = await observationSurface({
5728
- projectId: resolvedProjectId,
5729
- workflowId: resolvedWorkflowId,
5730
- workflowRunId: resolvedWorkflowRunId,
5731
- assetName: normalizedAssetName,
5732
- assetType: normalizedAssetType,
5733
- sourceRef: normalizedSourceRef,
5734
- submittedBy: normalizedSubmittedBy,
5735
- observationKind: 'asset_intake',
5736
- observationState: 'registered',
5737
- observationSummary: normalizedBusinessContext || normalizedHandoffNotes || normalizedAssetName || normalizedSourceRef,
5738
- evidenceRefs: normalizedEvidenceRefs,
5739
- metadata: {
5740
- ...normalizedMetadata,
5741
- source: 'registerModernizationAsset',
5742
- },
5743
- });
5744
- }
5745
-
5746
- let transferResult = null;
5747
- let watchResult = null;
5748
- const classifierTransferNeeded = Boolean(normalizedClassificationRequired && normalizedAssignedClassifier);
5749
- if (classifierTransferNeeded) {
5750
- const transferSurface = typeof this.transferWorkPacket === 'function'
5751
- ? this.transferWorkPacket.bind(this)
5752
- : null;
5753
- if (!transferSurface) {
5754
- missingSurfaces.push('transferWorkPacket');
5755
- } else {
5756
- transferResult = await transferSurface({
5757
- workflowRunId: resolvedWorkflowRunId,
5758
- transferKind: 'upstream_remediation',
5759
- objective: `Classify modernization asset ${normalizedAssetName || normalizedSourceRef}`,
5760
- requestedOutcome: 'Review and classify the modernization asset intake.',
5761
- target: {
5762
- intent: 'upstream_remediation',
5763
- recipient_mode: 'agent_session',
5764
- preferred_agent_session_id: normalizedAssignedClassifier,
5765
- preferred_role_key: normalizedAssignedClassifier,
5766
- },
5767
- artifacts: normalizedSourceRef ? [normalizedSourceRef] : [],
5768
- issues: normalizedKnownRisks,
5769
- expected_evidence: normalizedEvidenceRefs,
5770
- preferred_modes: ['bundle', 'artifact_refs', 'inline_payload'],
5771
- capabilities: {},
5772
- sender_agent_session_id: normalizedSubmittedBy,
5773
- sender_actor_session_id: null,
5774
- message_kind: 'handoff',
5775
- subject: normalizedAssetName || normalizedSourceRef || 'Modernization asset intake',
5776
- body_markdown: normalizedHandoffNotes || normalizedBusinessContext || normalizedSuspectedValue,
5777
- metadata: {
5778
- ...normalizedMetadata,
5779
- source: 'registerModernizationAsset',
5780
- asset_name: normalizedAssetName,
5781
- asset_type: normalizedAssetType,
5782
- source_ref: normalizedSourceRef,
5783
- origin_system: normalizedOriginSystem,
5784
- },
5785
- });
5786
- }
5787
- }
5788
-
5789
- const transferPayload = isPlainObject(transferResult) ? transferResult : {};
5790
- const transferPacketId = cleanText(transferPayload.work_transfer_packet?.work_transfer_packet_id)
5791
- || cleanText(transferPayload.work_transfer_packet_id)
5792
- || cleanText(transferPayload.transfer_packet_id)
5793
- || cleanText(transferPayload.packet_id)
5794
- || null;
5795
- const transferChannelId = cleanText(transferPayload.communication_transfer_channel?.transfer_channel_id)
5796
- || cleanText(transferPayload.communication_transfer_channel?.channel_id)
5797
- || cleanText(transferPayload.transfer_channel?.transfer_channel_id)
5798
- || cleanText(transferPayload.transfer_channel_id)
5799
- || cleanText(transferPayload.channel_id)
5800
- || null;
5801
-
5802
- if (classifierTransferNeeded && transferChannelId && transferPacketId) {
5803
- const watchSurface = typeof this.startMessageWatch === 'function'
5804
- ? this.startMessageWatch.bind(this)
5805
- : null;
5806
- if (!watchSurface) {
5807
- missingSurfaces.push('startMessageWatch');
5808
- } else {
5809
- watchResult = await watchSurface({
5810
- transferChannelId,
5811
- workTransferPacketId: transferPacketId,
5812
- workflowRunId: resolvedWorkflowRunId,
5813
- watchingAgentRole: normalizedSubmittedBy,
5814
- expectedFromRole: normalizedAssignedClassifier,
5815
- expectedMessageKind: 'response',
5816
- watchType: 'expected_peer_message',
5817
- watchingAgentSessionId: normalizedSubmittedBy,
5818
- expectedPayload: {
5819
- source_ref: normalizedSourceRef,
5820
- asset_type: normalizedAssetType,
5821
- classification_required: Boolean(normalizedClassificationRequired),
5822
- },
5823
- currentStatus: 'watching',
5824
- operatorNudge: normalizedHandoffNotes || normalizedBusinessContext || normalizedSuspectedValue,
5825
- metadata: {
5826
- ...normalizedMetadata,
5827
- source: 'registerModernizationAsset',
5828
- },
5829
- });
5830
- }
5831
- }
5832
-
5833
- const watchPayload = isPlainObject(watchResult) ? watchResult : {};
5834
- const watchId = cleanText(watchPayload.message_watch_id)
5835
- || cleanText(watchPayload.watch_id)
5836
- || cleanText(watchPayload.message_watch?.message_watch_id)
5837
- || cleanText(watchPayload.message_watch?.watch_id)
5838
- || null;
5839
- const assetId = cleanText(assetIntakeRecord?.asset_id)
5840
- || cleanText(assetIntakeRecord?.assetId)
5841
- || cleanText(assetIntakeRecord?.intake_record_id)
5842
- || cleanText(assetIntakeRecord?.intakeRecordId)
5843
- || cleanText(assetIntakeRecord?.modernization_asset_id)
5844
- || cleanText(assetIntakeRecord?.modernizationAssetId)
5845
- || null;
5846
- const intakeRecordId = cleanText(assetIntakeRecord?.intake_record_id)
5847
- || cleanText(assetIntakeRecord?.intakeRecordId)
5848
- || cleanText(assetIntakeRecord?.asset_id)
5849
- || cleanText(assetIntakeRecord?.assetId)
5850
- || null;
5851
-
5852
- return {
5853
- status: missingSurfaces.length > 0 ? 'partial' : 'ready',
5854
- project_id: resolvedProjectId || null,
5855
- asset_id: assetId,
5856
- intake_record_id: intakeRecordId,
5857
- asset_type: normalizedAssetType,
5858
- source_ref: normalizedSourceRef,
5859
- submitted_by: normalizedSubmittedBy,
5860
- classification_required: Boolean(normalizedClassificationRequired),
5861
- assigned_classifier: normalizedAssignedClassifier || null,
5862
- transfer_packet_id: transferPacketId,
5863
- watch_id: watchId,
5864
- operator_projection_metadata: {
5865
- portfolio_bundle: portfolioBundle,
5866
- project_roadmap_summary: projectRoadmapSummary,
5867
- project_roadmap_active_item: projectActiveItem,
5868
- asset_intake_record: assetIntakeRecord,
5869
- intake_observation: intakeObservation,
5870
- transfer: transferPayload,
5871
- message_watch: watchPayload,
5872
- },
5873
- missing_surfaces: [...new Set(missingSurfaces)],
5874
- project: projectPayload,
5875
- transfer: transferPayload,
5876
- asset_intake_record: assetIntakeRecord,
5877
- intake_observation: intakeObservation,
5878
- message_watch: watchPayload,
5879
- };
5880
- }
5881
-
5882
- async classifyModernizationAsset({
5883
- projectIdentifier,
5884
- projectId,
5885
- relatedProjectId,
5886
- related_project_id,
5887
- startWorkBody = {},
5888
- startWorkRequest = {},
5889
- resumeProjectWorkOptions = {},
5890
- asset = {},
5891
- modernizationAsset = {},
5892
- classification = {},
5893
- modernizationClassification = {},
5894
- assetId,
5895
- asset_id,
5896
- intakeRecordId,
5897
- intake_record_id,
5898
- assetType,
5899
- asset_type,
5900
- sourceRef,
5901
- source_ref,
5902
- classificationCategory,
5903
- classification_category,
5904
- domainArea,
5905
- domain_area,
5906
- reusePotential,
5907
- reuse_potential,
5908
- modernizationNeed,
5909
- modernization_need,
5910
- knownRisks = [],
5911
- known_risks = [],
5912
- evidenceRefs = [],
5913
- evidence_refs = [],
5914
- recommendedNextStep,
5915
- recommended_next_step,
5916
- classificationConfidence,
5917
- classification_confidence,
5918
- reviewRequired,
5919
- review_required,
5920
- assignedReviewer,
5921
- assigned_reviewer,
5922
- handoffNotes,
5923
- handoff_notes,
5924
- submittedBy,
5925
- submitted_by,
5926
- includePortfolioBundle = true,
5927
- includeProjectRoadmap = true,
5928
- includeProjectActiveItem = true,
5929
- metadata = {},
5930
- } = {}) {
5931
- const missingSurfaces = [];
5932
- const normalizedMetadata = isPlainObject(metadata) ? metadata : {};
5933
- const normalizedAsset = isPlainObject(modernizationAsset)
5934
- ? modernizationAsset
5935
- : (isPlainObject(asset) ? asset : {});
5936
- const normalizedClassification = isPlainObject(modernizationClassification)
5937
- ? modernizationClassification
5938
- : (isPlainObject(classification) ? classification : {});
5939
- const normalizedProjectReference = cleanText(projectIdentifier)
5940
- || cleanText(projectId)
5941
- || cleanText(relatedProjectId)
5942
- || cleanText(related_project_id)
5943
- || cleanText(normalizedAsset.related_project_id)
5944
- || cleanText(normalizedAsset.relatedProjectId)
5945
- || cleanText(normalizedClassification.related_project_id)
5946
- || cleanText(normalizedClassification.relatedProjectId);
5947
- const normalizedAssetId = cleanText(asset_id)
5948
- || cleanText(assetId)
5949
- || cleanText(intake_record_id)
5950
- || cleanText(intakeRecordId)
5951
- || cleanText(normalizedAsset.asset_id)
5952
- || cleanText(normalizedAsset.assetId)
5953
- || cleanText(normalizedAsset.intake_record_id)
5954
- || cleanText(normalizedAsset.intakeRecordId);
5955
- const normalizedSourceRef = cleanText(source_ref)
5956
- || cleanText(sourceRef)
5957
- || cleanText(normalizedAsset.source_ref)
5958
- || cleanText(normalizedAsset.sourceRef)
5959
- || normalizedAssetId;
5960
- if (!normalizedAssetId && !normalizedSourceRef) {
5961
- throw new Error('asset_id, intake_record_id, or source_ref is required.');
5962
- }
5963
- const normalizedAssetType = cleanText(asset_type)
5964
- || cleanText(assetType)
5965
- || cleanText(normalizedAsset.asset_type)
5966
- || cleanText(normalizedAsset.assetType)
5967
- || 'unknown';
5968
- const normalizedClassificationCategory = cleanText(classification_category)
5969
- || cleanText(classificationCategory)
5970
- || cleanText(normalizedClassification.classification_category)
5971
- || cleanText(normalizedClassification.classificationCategory)
5972
- || 'uncategorized';
5973
- const normalizedDomainArea = cleanText(domain_area)
5974
- || cleanText(domainArea)
5975
- || cleanText(normalizedClassification.domain_area)
5976
- || cleanText(normalizedClassification.domainArea);
5977
- const normalizedReusePotential = cleanText(reuse_potential)
5978
- || cleanText(reusePotential)
5979
- || cleanText(normalizedClassification.reuse_potential)
5980
- || cleanText(normalizedClassification.reusePotential);
5981
- const normalizedModernizationNeed = cleanText(modernization_need)
5982
- || cleanText(modernizationNeed)
5983
- || cleanText(normalizedClassification.modernization_need)
5984
- || cleanText(normalizedClassification.modernizationNeed);
5985
- const normalizedRecommendedNextStep = cleanText(recommended_next_step)
5986
- || cleanText(recommendedNextStep)
5987
- || cleanText(normalizedClassification.recommended_next_step)
5988
- || cleanText(normalizedClassification.recommendedNextStep);
5989
- const normalizedClassificationConfidence = cleanText(classification_confidence)
5990
- || cleanText(classificationConfidence)
5991
- || cleanText(normalizedClassification.classification_confidence)
5992
- || cleanText(normalizedClassification.classificationConfidence);
5993
- const normalizedKnownRisks = Array.isArray(known_risks)
5994
- ? known_risks
5995
- : (Array.isArray(knownRisks) ? knownRisks : normalizedAsset.known_risks || normalizedAsset.knownRisks || []);
5996
- const normalizedEvidenceRefs = Array.isArray(evidence_refs)
5997
- ? evidence_refs
5998
- : (Array.isArray(evidenceRefs) ? evidenceRefs : normalizedAsset.evidence_refs || normalizedAsset.evidenceRefs || []);
5999
- const normalizedHandoffNotes = cleanText(handoff_notes)
6000
- || cleanText(handoffNotes)
6001
- || cleanText(normalizedClassification.handoff_notes)
6002
- || cleanText(normalizedClassification.handoffNotes)
6003
- || cleanText(normalizedAsset.handoff_notes)
6004
- || cleanText(normalizedAsset.handoffNotes);
6005
- const normalizedAssignedReviewer = cleanText(assigned_reviewer)
6006
- || cleanText(assignedReviewer)
6007
- || cleanText(normalizedClassification.assigned_reviewer)
6008
- || cleanText(normalizedClassification.assignedReviewer);
6009
- const normalizedSubmittedBy = cleanText(submitted_by)
6010
- || cleanText(submittedBy)
6011
- || this.agentSessionId
6012
- || this.actorId
6013
- || null;
6014
- const normalizedReviewRequired = review_required ?? reviewRequired ?? true;
6015
-
6016
- let continuation = null;
6017
- let startWorkResult = null;
6018
- if (normalizedProjectReference) {
6019
- if (typeof this.resumeProjectWork === 'function') {
6020
- continuation = await this.resumeProjectWork({
6021
- projectIdentifier: normalizedProjectReference,
6022
- projectId: normalizedProjectReference,
6023
- requireClaim: false,
6024
- ...resumeProjectWorkOptions,
6025
- });
6026
- } else {
6027
- missingSurfaces.push('resumeProjectWork');
6028
- }
6029
- } else if (Object.keys(isPlainObject(startWorkRequest) ? startWorkRequest : {}).length > 0 || Object.keys(isPlainObject(startWorkBody) ? startWorkBody : {}).length > 0) {
6030
- if (typeof this.startWork === 'function') {
6031
- startWorkResult = await this.startWork({
6032
- ...(isPlainObject(startWorkBody) ? startWorkBody : {}),
6033
- ...(isPlainObject(startWorkRequest) ? startWorkRequest : {}),
6034
- });
6035
- } else {
6036
- missingSurfaces.push('startWork');
6037
- }
6038
- }
6039
-
6040
- const projectPayload = isPlainObject(continuation?.project)
6041
- ? continuation.project
6042
- : isPlainObject(startWorkResult?.project)
6043
- ? startWorkResult.project
6044
- : isPlainObject(continuation?.summary)
6045
- ? continuation.summary
6046
- : {};
6047
- const resolvedProjectId = cleanText(projectPayload.project_id)
6048
- || cleanText(projectPayload.projectId)
6049
- || normalizedProjectReference;
6050
- const resolvedWorkflowId = cleanText(projectPayload.workflow_id)
6051
- || cleanText(projectPayload.workflowId)
6052
- || cleanText(continuation?.workflow_id)
6053
- || cleanText(startWorkResult?.workflow_id);
6054
- const resolvedWorkflowRunId = cleanText(projectPayload.workflow_run_id)
6055
- || cleanText(projectPayload.workflowRunId)
6056
- || cleanText(continuation?.workflow_run_id)
6057
- || cleanText(startWorkResult?.workflow_run_id)
6058
- || cleanText(startWorkResult?.workflowRunId);
6059
-
6060
- const portfolioBundle = includePortfolioBundle && typeof this.getPortfolioBundle === 'function'
6061
- ? await this.getPortfolioBundle().catch(() => null)
6062
- : null;
6063
- const projectRoadmapSummary = resolvedProjectId && includeProjectRoadmap && typeof this.getProjectRoadmapSummary === 'function'
6064
- ? await this.getProjectRoadmapSummary(resolvedProjectId).catch(() => null)
6065
- : null;
6066
- const projectActiveItem = resolvedProjectId && includeProjectActiveItem && typeof this.getProjectRoadmapActiveItem === 'function'
6067
- ? await this.getProjectRoadmapActiveItem(resolvedProjectId).catch(() => null)
6068
- : null;
6069
-
6070
- let registeredAssetRecord = null;
6071
- const intakeLookupSurface = typeof this.getModernizationAssetIntakeRecord === 'function'
6072
- ? this.getModernizationAssetIntakeRecord.bind(this)
6073
- : null;
6074
- if (!intakeLookupSurface) {
6075
- missingSurfaces.push('getModernizationAssetIntakeRecord');
6076
- } else {
6077
- registeredAssetRecord = await intakeLookupSurface({
6078
- projectId: resolvedProjectId,
6079
- workflowId: resolvedWorkflowId,
6080
- workflowRunId: resolvedWorkflowRunId,
6081
- assetId: normalizedAssetId,
6082
- intakeRecordId: cleanText(intake_record_id) || cleanText(intakeRecordId),
6083
- sourceRef: normalizedSourceRef,
6084
- metadata: {
6085
- ...normalizedMetadata,
6086
- source: 'classifyModernizationAsset',
6087
- },
6088
- });
6089
- }
6090
-
6091
- let classificationRecord = null;
6092
- const classificationSurface = typeof this.createModernizationAssetClassificationRecord === 'function'
6093
- ? this.createModernizationAssetClassificationRecord.bind(this)
6094
- : null;
6095
- if (!classificationSurface) {
6096
- missingSurfaces.push('createModernizationAssetClassificationRecord');
6097
- } else {
6098
- classificationRecord = await classificationSurface({
6099
- projectId: resolvedProjectId,
6100
- workflowId: resolvedWorkflowId,
6101
- workflowRunId: resolvedWorkflowRunId,
6102
- assetId: normalizedAssetId,
6103
- intakeRecordId: cleanText(intake_record_id) || cleanText(intakeRecordId),
6104
- assetType: normalizedAssetType,
6105
- sourceRef: normalizedSourceRef,
6106
- classificationCategory: normalizedClassificationCategory,
6107
- domainArea: normalizedDomainArea,
6108
- reusePotential: normalizedReusePotential,
6109
- modernizationNeed: normalizedModernizationNeed,
6110
- knownRisks: normalizedKnownRisks,
6111
- recommendedNextStep: normalizedRecommendedNextStep,
6112
- classificationConfidence: normalizedClassificationConfidence,
6113
- evidenceRefs: normalizedEvidenceRefs,
6114
- reviewRequired: Boolean(normalizedReviewRequired),
6115
- assignedReviewer: normalizedAssignedReviewer,
6116
- submittedBy: normalizedSubmittedBy,
6117
- handoffNotes: normalizedHandoffNotes,
6118
- metadata: {
6119
- ...normalizedMetadata,
6120
- source: 'classifyModernizationAsset',
6121
- },
6122
- });
6123
- }
6124
-
6125
- let classificationObservation = null;
6126
- const observationSurface = typeof this.submitModernizationAssetClassificationObservation === 'function'
6127
- ? this.submitModernizationAssetClassificationObservation.bind(this)
6128
- : null;
6129
- if (!observationSurface) {
6130
- missingSurfaces.push('submitModernizationAssetClassificationObservation');
6131
- } else {
6132
- classificationObservation = await observationSurface({
6133
- projectId: resolvedProjectId,
6134
- workflowId: resolvedWorkflowId,
6135
- workflowRunId: resolvedWorkflowRunId,
6136
- assetId: normalizedAssetId,
6137
- intakeRecordId: cleanText(intake_record_id) || cleanText(intakeRecordId),
6138
- assetType: normalizedAssetType,
6139
- sourceRef: normalizedSourceRef,
6140
- classificationCategory: normalizedClassificationCategory,
6141
- classificationConfidence: normalizedClassificationConfidence,
6142
- evidenceRefs: normalizedEvidenceRefs,
6143
- submittedBy: normalizedSubmittedBy,
6144
- observationKind: 'asset_classification',
6145
- observationState: 'prepared',
6146
- observationSummary: normalizedClassificationCategory || normalizedDomainArea || normalizedAssetType || normalizedSourceRef,
6147
- metadata: {
6148
- ...normalizedMetadata,
6149
- source: 'classifyModernizationAsset',
6150
- },
6151
- });
6152
- }
6153
-
6154
- let transferResult = null;
6155
- let watchResult = null;
6156
- let heartbeatResult = null;
6157
- const reviewTransferNeeded = Boolean(normalizedReviewRequired && normalizedAssignedReviewer);
6158
- if (reviewTransferNeeded) {
6159
- const transferSurface = typeof this.transferWorkPacket === 'function'
6160
- ? this.transferWorkPacket.bind(this)
6161
- : null;
6162
- if (!transferSurface) {
6163
- missingSurfaces.push('transferWorkPacket');
6164
- } else {
6165
- transferResult = await transferSurface({
6166
- workflowRunId: resolvedWorkflowRunId,
6167
- transferKind: 'upstream_remediation',
6168
- objective: `Review modernization asset classification ${normalizedSourceRef || normalizedAssetId}`,
6169
- requestedOutcome: 'Review and enrich the modernization asset classification.',
6170
- target: {
6171
- intent: 'upstream_remediation',
6172
- recipient_mode: 'agent_session',
6173
- preferred_agent_session_id: normalizedAssignedReviewer,
6174
- preferred_role_key: normalizedAssignedReviewer,
6175
- },
6176
- artifacts: normalizedSourceRef ? [normalizedSourceRef] : [],
6177
- issues: normalizedKnownRisks,
6178
- expected_evidence: normalizedEvidenceRefs,
6179
- preferred_modes: ['bundle', 'artifact_refs', 'inline_payload'],
6180
- capabilities: {},
6181
- sender_agent_session_id: normalizedSubmittedBy,
6182
- sender_actor_session_id: null,
6183
- message_kind: 'handoff',
6184
- subject: normalizedClassificationCategory || normalizedSourceRef || 'Modernization asset classification',
6185
- body_markdown: normalizedHandoffNotes || normalizedRecommendedNextStep || normalizedClassificationCategory,
6186
- metadata: {
6187
- ...normalizedMetadata,
6188
- source: 'classifyModernizationAsset',
6189
- asset_id: normalizedAssetId,
6190
- intake_record_id: cleanText(intake_record_id) || cleanText(intakeRecordId),
6191
- asset_type: normalizedAssetType,
6192
- classification_category: normalizedClassificationCategory,
6193
- domain_area: normalizedDomainArea,
6194
- },
6195
- });
6196
- }
6197
- }
6198
-
6199
- const transferPayload = isPlainObject(transferResult) ? transferResult : {};
6200
- const transferPacketId = cleanText(transferPayload.work_transfer_packet?.work_transfer_packet_id)
6201
- || cleanText(transferPayload.work_transfer_packet_id)
6202
- || cleanText(transferPayload.transfer_packet_id)
6203
- || cleanText(transferPayload.packet_id)
6204
- || null;
6205
- const transferChannelId = cleanText(transferPayload.communication_transfer_channel?.transfer_channel_id)
6206
- || cleanText(transferPayload.communication_transfer_channel?.channel_id)
6207
- || cleanText(transferPayload.transfer_channel?.transfer_channel_id)
6208
- || cleanText(transferPayload.transfer_channel_id)
6209
- || cleanText(transferPayload.channel_id)
6210
- || null;
6211
-
6212
- if (reviewTransferNeeded && transferChannelId && transferPacketId) {
6213
- const watchSurface = typeof this.startMessageWatch === 'function'
6214
- ? this.startMessageWatch.bind(this)
6215
- : null;
6216
- if (!watchSurface) {
6217
- missingSurfaces.push('startMessageWatch');
6218
- } else {
6219
- watchResult = await watchSurface({
6220
- transferChannelId,
6221
- workTransferPacketId: transferPacketId,
6222
- workflowRunId: resolvedWorkflowRunId,
6223
- watchingAgentRole: normalizedSubmittedBy,
6224
- expectedFromRole: normalizedAssignedReviewer,
6225
- expectedMessageKind: 'response',
6226
- watchType: 'expected_peer_message',
6227
- watchingAgentSessionId: normalizedSubmittedBy,
6228
- expectedPayload: {
6229
- asset_id: normalizedAssetId,
6230
- classification_category: normalizedClassificationCategory,
6231
- review_required: Boolean(normalizedReviewRequired),
6232
- },
6233
- currentStatus: 'watching',
6234
- operatorNudge: normalizedHandoffNotes || normalizedRecommendedNextStep || normalizedClassificationCategory,
6235
- metadata: {
6236
- ...normalizedMetadata,
6237
- source: 'classifyModernizationAsset',
6238
- },
6239
- });
6240
- }
6241
- }
6242
-
6243
- const watchPayload = isPlainObject(watchResult) ? watchResult : {};
6244
- const watchId = cleanText(watchPayload.message_watch_id)
6245
- || cleanText(watchPayload.watch_id)
6246
- || cleanText(watchPayload.message_watch?.message_watch_id)
6247
- || cleanText(watchPayload.message_watch?.watch_id)
6248
- || null;
6249
-
6250
- if ((classificationRecord || classificationObservation || transferResult) && typeof this.postCollaborationHeartbeat === 'function') {
6251
- try {
6252
- heartbeatResult = await this.postCollaborationHeartbeat({
6253
- transferChannelId: transferChannelId || undefined,
6254
- workTransferPacketId: transferPacketId || undefined,
6255
- workflowRunId: resolvedWorkflowRunId,
6256
- participantRole: normalizedReviewRequired && normalizedAssignedReviewer ? 'classifier' : 'warehouse',
6257
- currentPhase: reviewTransferNeeded ? 'review_pending' : 'classification_prepared',
6258
- currentTaskSummary: normalizedClassificationCategory || normalizedSourceRef || 'Modernization asset classification',
6259
- });
6260
- } catch (error) {
6261
- void error;
6262
- }
6263
- }
6264
-
6265
- const heartbeatPayload = isPlainObject(heartbeatResult) ? heartbeatResult : {};
6266
- const classificationId = cleanText(classificationRecord?.classification_id)
6267
- || cleanText(classificationRecord?.classificationId)
6268
- || cleanText(classificationRecord?.intake_classification_id)
6269
- || cleanText(classificationRecord?.intakeClassificationId)
6270
- || null;
6271
- const assetRecordId = cleanText(registeredAssetRecord?.asset_id)
6272
- || cleanText(registeredAssetRecord?.assetId)
6273
- || cleanText(registeredAssetRecord?.intake_record_id)
6274
- || cleanText(registeredAssetRecord?.intakeRecordId)
6275
- || cleanText(normalizedAssetId)
6276
- || null;
6277
- const intakeRecordResolvedId = cleanText(registeredAssetRecord?.intake_record_id)
6278
- || cleanText(registeredAssetRecord?.intakeRecordId)
6279
- || cleanText(registeredAssetRecord?.asset_id)
6280
- || cleanText(registeredAssetRecord?.assetId)
6281
- || cleanText(intake_record_id)
6282
- || cleanText(intakeRecordId)
6283
- || null;
6284
-
6285
- return {
6286
- status: missingSurfaces.length > 0 ? 'partial' : 'ready',
6287
- project_id: resolvedProjectId || null,
6288
- asset_id: assetRecordId,
6289
- intake_record_id: intakeRecordResolvedId,
6290
- classification_id: classificationId,
6291
- asset_type: normalizedAssetType,
6292
- classification_category: normalizedClassificationCategory,
6293
- reuse_potential: normalizedReusePotential || null,
6294
- risk_level: normalizedClassification.risk_level || normalizedClassification.riskLevel || normalizedKnownRisks[0] || null,
6295
- classification_confidence: normalizedClassificationConfidence || null,
6296
- assigned_reviewer: normalizedAssignedReviewer || null,
6297
- source_ref: normalizedSourceRef,
6298
- submitted_by: normalizedSubmittedBy,
6299
- review_required: Boolean(normalizedReviewRequired),
6300
- transfer_packet_id: transferPacketId,
6301
- watch_id: watchId,
6302
- heartbeat_status: cleanText(heartbeatPayload.activity_state)
6303
- || cleanText(heartbeatPayload.collaboration_heartbeat?.activity_state)
6304
- || cleanText(heartbeatPayload.status)
6305
- || cleanText(heartbeatPayload.heartbeat_status)
6306
- || null,
6307
- operator_projection_metadata: {
6308
- portfolio_bundle: portfolioBundle,
6309
- project_roadmap_summary: projectRoadmapSummary,
6310
- project_roadmap_active_item: projectActiveItem,
6311
- registered_asset_record: registeredAssetRecord,
6312
- classification_record: classificationRecord,
6313
- classification_observation: classificationObservation,
6314
- review_transfer: transferPayload,
6315
- message_watch: watchPayload,
6316
- heartbeat: heartbeatPayload,
6317
- },
6318
- missing_surfaces: [...new Set(missingSurfaces)],
6319
- project: projectPayload,
6320
- registered_asset_record: registeredAssetRecord,
6321
- classification_record: classificationRecord,
6322
- classification_observation: classificationObservation,
6323
- transfer: transferPayload,
6324
- message_watch: watchPayload,
6325
- heartbeat: heartbeatPayload,
6326
- };
6327
- }
6328
-
6329
- async discoverSalvageCandidates({
6330
- projectIdentifier,
6331
- projectId,
6332
- relatedProjectId,
6333
- related_project_id,
6334
- startWorkBody = {},
6335
- startWorkRequest = {},
6336
- resumeProjectWorkOptions = {},
6337
- asset = {},
6338
- modernizationAsset = {},
6339
- classification = {},
6340
- modernizationClassification = {},
6341
- assetId,
6342
- asset_id,
6343
- intakeRecordId,
6344
- intake_record_id,
6345
- classificationId,
6346
- classification_id,
6347
- sourceRef,
6348
- source_ref,
6349
- assetType,
6350
- asset_type,
6351
- classificationCategory,
6352
- classification_category,
6353
- domainArea,
6354
- domain_area,
6355
- reusePotential,
6356
- reuse_potential,
6357
- modernizationNeed,
6358
- modernization_need,
6359
- riskLevel,
6360
- risk_level,
6361
- knownRisks = [],
6362
- known_risks = [],
6363
- evidenceRefs = [],
6364
- evidence_refs = [],
6365
- recommendedNextStep,
6366
- recommended_next_step,
6367
- reviewRequired,
6368
- review_required,
6369
- assignedReviewer,
6370
- assigned_reviewer,
6371
- candidateSearchIntent,
6372
- candidate_search_intent,
6373
- candidateSearchQuery,
6374
- candidate_search_query,
6375
- relatedSymbols = [],
6376
- related_symbols = [],
6377
- relatedQualifiedNames = [],
6378
- related_qualified_names = [],
6379
- relatedFilePaths = [],
6380
- related_file_paths = [],
6381
- maxCandidates = 10,
6382
- submittedBy,
6383
- submitted_by,
6384
- includePortfolioBundle = true,
6385
- includeProjectRoadmap = true,
6386
- includeProjectActiveItem = true,
6387
- metadata = {},
6388
- } = {}) {
6389
- const missingSurfaces = [];
6390
- const normalizedMetadata = isPlainObject(metadata) ? metadata : {};
6391
- const normalizedAsset = isPlainObject(modernizationAsset)
6392
- ? modernizationAsset
6393
- : (isPlainObject(asset) ? asset : {});
6394
- const normalizedClassification = isPlainObject(modernizationClassification)
6395
- ? modernizationClassification
6396
- : (isPlainObject(classification) ? classification : {});
6397
- const normalizedProjectReference = cleanText(projectIdentifier)
6398
- || cleanText(projectId)
6399
- || cleanText(relatedProjectId)
6400
- || cleanText(related_project_id)
6401
- || cleanText(normalizedAsset.related_project_id)
6402
- || cleanText(normalizedAsset.relatedProjectId)
6403
- || cleanText(normalizedClassification.related_project_id)
6404
- || cleanText(normalizedClassification.relatedProjectId);
6405
- const normalizedAssetId = cleanText(asset_id)
6406
- || cleanText(assetId)
6407
- || cleanText(intake_record_id)
6408
- || cleanText(intakeRecordId)
6409
- || cleanText(normalizedAsset.asset_id)
6410
- || cleanText(normalizedAsset.assetId)
6411
- || cleanText(normalizedAsset.intake_record_id)
6412
- || cleanText(normalizedAsset.intakeRecordId);
6413
- const normalizedClassificationId = cleanText(classification_id)
6414
- || cleanText(classificationId)
6415
- || cleanText(normalizedClassification.classification_id)
6416
- || cleanText(normalizedClassification.classificationId);
6417
- const normalizedSourceRef = cleanText(source_ref)
6418
- || cleanText(sourceRef)
6419
- || cleanText(normalizedAsset.source_ref)
6420
- || cleanText(normalizedAsset.sourceRef)
6421
- || normalizedAssetId;
6422
- if (!normalizedAssetId && !normalizedSourceRef && !normalizedClassificationId) {
6423
- throw new Error('asset_id, intake_record_id, classification_id, or source_ref is required.');
6424
- }
6425
- const normalizedAssetType = cleanText(asset_type)
6426
- || cleanText(assetType)
6427
- || cleanText(normalizedAsset.asset_type)
6428
- || cleanText(normalizedAsset.assetType)
6429
- || 'unknown';
6430
- const normalizedClassificationCategory = cleanText(classification_category)
6431
- || cleanText(classificationCategory)
6432
- || cleanText(normalizedClassification.classification_category)
6433
- || cleanText(normalizedClassification.classificationCategory)
6434
- || 'uncategorized';
6435
- const normalizedDomainArea = cleanText(domain_area)
6436
- || cleanText(domainArea)
6437
- || cleanText(normalizedClassification.domain_area)
6438
- || cleanText(normalizedClassification.domainArea);
6439
- const normalizedReusePotential = cleanText(reuse_potential)
6440
- || cleanText(reusePotential)
6441
- || cleanText(normalizedClassification.reuse_potential)
6442
- || cleanText(normalizedClassification.reusePotential);
6443
- const normalizedModernizationNeed = cleanText(modernization_need)
6444
- || cleanText(modernizationNeed)
6445
- || cleanText(normalizedClassification.modernization_need)
6446
- || cleanText(normalizedClassification.modernizationNeed);
6447
- const normalizedRiskLevel = cleanText(risk_level)
6448
- || cleanText(riskLevel)
6449
- || cleanText(normalizedClassification.risk_level)
6450
- || cleanText(normalizedClassification.riskLevel)
6451
- || null;
6452
- const normalizedRecommendedNextStep = cleanText(recommended_next_step)
6453
- || cleanText(recommendedNextStep)
6454
- || cleanText(normalizedClassification.recommended_next_step)
6455
- || cleanText(normalizedClassification.recommendedNextStep);
6456
- const normalizedKnownRisks = Array.isArray(known_risks)
6457
- ? known_risks
6458
- : (Array.isArray(knownRisks) ? knownRisks : normalizedAsset.known_risks || normalizedAsset.knownRisks || []);
6459
- const normalizedEvidenceRefs = Array.isArray(evidence_refs)
6460
- ? evidence_refs
6461
- : (Array.isArray(evidenceRefs) ? evidenceRefs : normalizedAsset.evidence_refs || normalizedAsset.evidenceRefs || []);
6462
- const normalizedAssignedReviewer = cleanText(assigned_reviewer)
6463
- || cleanText(assignedReviewer)
6464
- || cleanText(normalizedClassification.assigned_reviewer)
6465
- || cleanText(normalizedClassification.assignedReviewer);
6466
- const normalizedSubmittedBy = cleanText(submitted_by)
6467
- || cleanText(submittedBy)
6468
- || this.agentSessionId
6469
- || this.actorId
6470
- || null;
6471
- const normalizedReviewRequired = review_required ?? reviewRequired ?? true;
6472
- const normalizedCandidateSearchIntent = cleanText(candidate_search_intent)
6473
- || cleanText(candidateSearchIntent)
6474
- || normalizedClassificationCategory
6475
- || normalizedDomainArea
6476
- || normalizedSourceRef
6477
- || normalizedAssetType;
6478
- const normalizedCandidateSearchQuery = cleanText(candidate_search_query)
6479
- || cleanText(candidateSearchQuery)
6480
- || [
6481
- normalizedClassificationCategory,
6482
- normalizedDomainArea,
6483
- normalizedModernizationNeed,
6484
- normalizedSourceRef,
6485
- normalizedAssetType,
6486
- ].filter(Boolean).join(' ');
6487
- const normalizedRelatedSymbols = cleanList(related_symbols.length > 0 ? related_symbols : relatedSymbols);
6488
- const normalizedRelatedQualifiedNames = cleanList(related_qualified_names.length > 0 ? related_qualified_names : relatedQualifiedNames);
6489
- const normalizedRelatedFilePaths = cleanList(related_file_paths.length > 0 ? related_file_paths : relatedFilePaths);
6490
- const normalizedMaxCandidates = Number.isFinite(Number(maxCandidates)) && Number(maxCandidates) > 0
6491
- ? Number(maxCandidates)
6492
- : 10;
6493
-
6494
- let continuation = null;
6495
- let startWorkResult = null;
6496
- if (normalizedProjectReference) {
6497
- if (typeof this.resumeProjectWork === 'function') {
6498
- continuation = await this.resumeProjectWork({
6499
- projectIdentifier: normalizedProjectReference,
6500
- projectId: normalizedProjectReference,
6501
- requireClaim: false,
6502
- ...resumeProjectWorkOptions,
6503
- });
6504
- } else {
6505
- missingSurfaces.push('resumeProjectWork');
6506
- }
6507
- } else if (Object.keys(isPlainObject(startWorkRequest) ? startWorkRequest : {}).length > 0 || Object.keys(isPlainObject(startWorkBody) ? startWorkBody : {}).length > 0) {
6508
- if (typeof this.startWork === 'function') {
6509
- startWorkResult = await this.startWork({
6510
- ...(isPlainObject(startWorkBody) ? startWorkBody : {}),
6511
- ...(isPlainObject(startWorkRequest) ? startWorkRequest : {}),
6512
- });
6513
- } else {
6514
- missingSurfaces.push('startWork');
6515
- }
6516
- }
6517
-
6518
- const projectPayload = isPlainObject(continuation?.project)
6519
- ? continuation.project
6520
- : isPlainObject(startWorkResult?.project)
6521
- ? startWorkResult.project
6522
- : isPlainObject(continuation?.summary)
6523
- ? continuation.summary
6524
- : {};
6525
- const resolvedProjectId = cleanText(projectPayload.project_id)
6526
- || cleanText(projectPayload.projectId)
6527
- || normalizedProjectReference;
6528
- const resolvedWorkflowId = cleanText(projectPayload.workflow_id)
6529
- || cleanText(projectPayload.workflowId)
6530
- || cleanText(continuation?.workflow_id)
6531
- || cleanText(startWorkResult?.workflow_id);
6532
- const resolvedWorkflowRunId = cleanText(projectPayload.workflow_run_id)
6533
- || cleanText(projectPayload.workflowRunId)
6534
- || cleanText(continuation?.workflow_run_id)
6535
- || cleanText(startWorkResult?.workflow_run_id)
6536
- || cleanText(startWorkResult?.workflowRunId);
6537
-
6538
- const portfolioBundle = includePortfolioBundle && typeof this.getPortfolioBundle === 'function'
6539
- ? await this.getPortfolioBundle().catch(() => null)
6540
- : null;
6541
- const projectRoadmapSummary = resolvedProjectId && includeProjectRoadmap && typeof this.getProjectRoadmapSummary === 'function'
6542
- ? await this.getProjectRoadmapSummary(resolvedProjectId).catch(() => null)
6543
- : null;
6544
- const projectActiveItem = resolvedProjectId && includeProjectActiveItem && typeof this.getProjectRoadmapActiveItem === 'function'
6545
- ? await this.getProjectRoadmapActiveItem(resolvedProjectId).catch(() => null)
6546
- : null;
6547
-
6548
- let assetIntakeRecord = null;
6549
- const intakeLookupSurface = typeof this.getModernizationAssetIntakeRecord === 'function'
6550
- ? this.getModernizationAssetIntakeRecord.bind(this)
6551
- : null;
6552
- if (!intakeLookupSurface) {
6553
- missingSurfaces.push('getModernizationAssetIntakeRecord');
6554
- } else {
6555
- assetIntakeRecord = await intakeLookupSurface({
6556
- projectId: resolvedProjectId,
6557
- workflowId: resolvedWorkflowId,
6558
- workflowRunId: resolvedWorkflowRunId,
6559
- assetId: normalizedAssetId,
6560
- intakeRecordId: cleanText(intake_record_id) || cleanText(intakeRecordId),
6561
- sourceRef: normalizedSourceRef,
6562
- metadata: {
6563
- ...normalizedMetadata,
6564
- source: 'discoverSalvageCandidates',
6565
- },
6566
- });
6567
- }
6568
-
6569
- let classificationRecord = null;
6570
- const classificationLookupSurface = typeof this.getModernizationAssetClassificationRecord === 'function'
6571
- ? this.getModernizationAssetClassificationRecord.bind(this)
6572
- : null;
6573
- if (!classificationLookupSurface) {
6574
- missingSurfaces.push('getModernizationAssetClassificationRecord');
6575
- } else {
6576
- classificationRecord = await classificationLookupSurface({
6577
- projectId: resolvedProjectId,
6578
- workflowId: resolvedWorkflowId,
6579
- workflowRunId: resolvedWorkflowRunId,
6580
- assetId: normalizedAssetId,
6581
- classificationId: normalizedClassificationId,
6582
- sourceRef: normalizedSourceRef,
6583
- metadata: {
6584
- ...normalizedMetadata,
6585
- source: 'discoverSalvageCandidates',
6586
- },
6587
- });
6588
- }
6589
-
6590
- let codeIntentResult = null;
6591
- if (typeof this.searchCodeByIntent === 'function') {
6592
- codeIntentResult = await this.searchCodeByIntent({
6593
- intentText: normalizedCandidateSearchIntent,
6594
- repoKey: normalizedProjectReference || undefined,
6595
- refName: normalizedSourceRef || undefined,
6596
- maxCandidates: normalizedMaxCandidates,
6597
- });
6598
- } else {
6599
- missingSurfaces.push('searchCodeByIntent');
6600
- }
6601
-
6602
- if (typeof this.getCodeContextForIntent === 'function') {
6603
- await this.getCodeContextForIntent({
6604
- intentText: normalizedCandidateSearchIntent,
6605
- candidatePolicy: 'top_ranked',
6606
- }).catch(() => null);
6607
- } else {
6608
- missingSurfaces.push('getCodeContextForIntent');
6609
- }
6610
-
6611
- let symbolSearchResult = null;
6612
- if (typeof this.searchSymbols === 'function') {
6613
- symbolSearchResult = await this.searchSymbols({
6614
- query: normalizedCandidateSearchQuery,
6615
- projectScope: resolvedProjectId || undefined,
6616
- maxResults: normalizedMaxCandidates,
6617
- }).catch(() => null);
6618
- } else {
6619
- missingSurfaces.push('searchSymbols');
6620
- }
6621
-
6622
- const relatedCodeResults = [];
6623
- if (typeof this.getRelatedCode === 'function') {
6624
- for (const qualifiedName of normalizedRelatedQualifiedNames) {
6625
- relatedCodeResults.push(await this.getRelatedCode({
6626
- qualifiedName,
6627
- relationshipType: 'salvage_candidate',
6628
- depth: 2,
6629
- includeCode: false,
6630
- requestedBy: normalizedSubmittedBy,
6631
- }).catch(() => null));
6632
- }
6633
- for (const symbolKey of normalizedRelatedSymbols) {
6634
- relatedCodeResults.push(await this.getRelatedCode({
6635
- symbolKey,
6636
- relationshipType: 'salvage_candidate',
6637
- depth: 2,
6638
- includeCode: false,
6639
- requestedBy: normalizedSubmittedBy,
6640
- }).catch(() => null));
6641
- }
6642
- } else if (normalizedRelatedSymbols.length > 0 || normalizedRelatedQualifiedNames.length > 0) {
6643
- missingSurfaces.push('getRelatedCode');
6644
- }
6645
-
6646
- const toArray = (value) => {
6647
- if (Array.isArray(value)) return value;
6648
- if (isPlainObject(value)) {
6649
- const candidates = value.candidates || value.results || value.items || value.symbols || value.related_code || value.relatedCode;
6650
- if (Array.isArray(candidates)) return candidates;
6651
- }
6652
- return [];
6653
- };
6654
-
6655
- const mapCandidate = (entry, candidateType, fallbackRankReason) => {
6656
- if (!isPlainObject(entry)) return null;
6657
- const sourceRefValue = cleanText(entry.source_ref)
6658
- || cleanText(entry.sourceRef)
6659
- || cleanText(entry.qualified_name)
6660
- || cleanText(entry.qualifiedName)
6661
- || cleanText(entry.file_path)
6662
- || cleanText(entry.filePath)
6663
- || cleanText(entry.symbol_key)
6664
- || cleanText(entry.symbolKey)
6665
- || cleanText(entry.symbol_id)
6666
- || cleanText(entry.symbolId)
6667
- || null;
6668
- const candidate = {
6669
- candidate_type: cleanText(entry.candidate_type) || cleanText(entry.candidateType) || candidateType,
6670
- source_ref: sourceRefValue,
6671
- file_path: cleanText(entry.file_path) || cleanText(entry.filePath) || null,
6672
- qualified_name: cleanText(entry.qualified_name) || cleanText(entry.qualifiedName) || null,
6673
- responsibility_area: cleanText(entry.responsibility_area) || cleanText(entry.responsibilityArea) || normalizedDomainArea || normalizedClassificationCategory || null,
6674
- reuse_potential: cleanText(entry.reuse_potential) || cleanText(entry.reusePotential) || normalizedReusePotential || null,
6675
- modernization_need: cleanText(entry.modernization_need) || cleanText(entry.modernizationNeed) || normalizedModernizationNeed || null,
6676
- risk_level: cleanText(entry.risk_level) || cleanText(entry.riskLevel) || normalizedRiskLevel || null,
6677
- rank_reason: cleanText(entry.rank_reason) || cleanText(entry.rankReason) || fallbackRankReason,
6678
- evidence_refs: Array.isArray(entry.evidence_refs) ? entry.evidence_refs : (Array.isArray(entry.evidenceRefs) ? entry.evidenceRefs : [...normalizedEvidenceRefs]),
6679
- recommended_next_step: cleanText(entry.recommended_next_step) || cleanText(entry.recommendedNextStep) || normalizedRecommendedNextStep || null,
6680
- };
6681
- return candidate.source_ref || candidate.file_path || candidate.qualified_name ? candidate : null;
6682
- };
6683
-
6684
- const candidateMap = new Map();
6685
- const addCandidates = (entries, candidateType, fallbackRankReason) => {
6686
- for (const entry of entries) {
6687
- const candidate = mapCandidate(entry, candidateType, fallbackRankReason);
6688
- if (!candidate) continue;
6689
- const key = candidate.source_ref || candidate.file_path || candidate.qualified_name;
6690
- if (!candidateMap.has(key)) {
6691
- candidateMap.set(key, candidate);
6692
- }
6693
- }
6694
- };
6695
-
6696
- addCandidates(toArray(codeIntentResult), 'intent_search_match', 'ranked by intent search');
6697
- addCandidates(toArray(symbolSearchResult), 'symbol_search_match', 'ranked by inventory symbol search');
6698
- for (const relatedCodeResult of relatedCodeResults) {
6699
- addCandidates(toArray(relatedCodeResult), 'related_code_match', 'related code lookup');
6700
- }
6701
-
6702
- const candidates = [...candidateMap.values()].slice(0, normalizedMaxCandidates);
6703
- const candidateCount = candidates.length;
6704
-
6705
- let discoveryObservation = null;
6706
- const discoveryObservationSurface = typeof this.submitModernizationSalvageDiscoveryObservation === 'function'
6707
- ? this.submitModernizationSalvageDiscoveryObservation.bind(this)
6708
- : null;
6709
- if (!discoveryObservationSurface) {
6710
- missingSurfaces.push('submitModernizationSalvageDiscoveryObservation');
6711
- } else {
6712
- discoveryObservation = await discoveryObservationSurface({
6713
- projectId: resolvedProjectId,
6714
- workflowId: resolvedWorkflowId,
6715
- workflowRunId: resolvedWorkflowRunId,
6716
- assetId: normalizedAssetId,
6717
- intakeRecordId: cleanText(intake_record_id) || cleanText(intakeRecordId),
6718
- classificationId: normalizedClassificationId,
6719
- candidateCount,
6720
- candidates,
6721
- evidenceRefs: normalizedEvidenceRefs,
6722
- observationKind: 'salvage_candidate_discovery',
6723
- observationState: 'prepared',
6724
- observationSummary: normalizedCandidateSearchIntent || normalizedClassificationCategory || normalizedSourceRef || 'salvage candidate discovery',
6725
- metadata: {
6726
- ...normalizedMetadata,
6727
- source: 'discoverSalvageCandidates',
6728
- },
6729
- });
6730
- }
6731
-
6732
- let transferResult = null;
6733
- let watchResult = null;
6734
- let heartbeatResult = null;
6735
- const reviewTransferNeeded = Boolean(normalizedReviewRequired && normalizedAssignedReviewer);
6736
- if (reviewTransferNeeded) {
6737
- const transferSurface = typeof this.transferWorkPacket === 'function'
6738
- ? this.transferWorkPacket.bind(this)
6739
- : null;
6740
- if (!transferSurface) {
6741
- missingSurfaces.push('transferWorkPacket');
6742
- } else {
6743
- transferResult = await transferSurface({
6744
- workflowRunId: resolvedWorkflowRunId,
6745
- transferKind: 'upstream_remediation',
6746
- objective: `Review salvage candidates for ${normalizedSourceRef || normalizedAssetId}`,
6747
- requestedOutcome: 'Review and rank salvage candidates from governed inventory.',
6748
- target: {
6749
- intent: 'upstream_remediation',
6750
- recipient_mode: 'agent_session',
6751
- preferred_agent_session_id: normalizedAssignedReviewer,
6752
- preferred_role_key: normalizedAssignedReviewer,
6753
- },
6754
- artifacts: normalizedSourceRef ? [normalizedSourceRef] : [],
6755
- issues: normalizedKnownRisks,
6756
- expected_evidence: normalizedEvidenceRefs,
6757
- preferred_modes: ['bundle', 'artifact_refs', 'inline_payload'],
6758
- capabilities: {},
6759
- sender_agent_session_id: normalizedSubmittedBy,
6760
- sender_actor_session_id: null,
6761
- message_kind: 'handoff',
6762
- subject: normalizedCandidateSearchIntent || normalizedSourceRef || 'Salvage candidate discovery',
6763
- body_markdown: normalizedRecommendedNextStep || normalizedClassificationCategory || normalizedSourceRef,
6764
- metadata: {
6765
- ...normalizedMetadata,
6766
- source: 'discoverSalvageCandidates',
6767
- asset_id: normalizedAssetId,
6768
- classification_id: normalizedClassificationId,
6769
- classification_category: normalizedClassificationCategory,
6770
- domain_area: normalizedDomainArea,
6771
- },
6772
- });
6773
- }
6774
- }
6775
-
6776
- const transferPayload = isPlainObject(transferResult) ? transferResult : {};
6777
- const transferPacketId = cleanText(transferPayload.work_transfer_packet?.work_transfer_packet_id)
6778
- || cleanText(transferPayload.work_transfer_packet_id)
6779
- || cleanText(transferPayload.transfer_packet_id)
6780
- || cleanText(transferPayload.packet_id)
6781
- || null;
6782
- const transferChannelId = cleanText(transferPayload.communication_transfer_channel?.transfer_channel_id)
6783
- || cleanText(transferPayload.communication_transfer_channel?.channel_id)
6784
- || cleanText(transferPayload.transfer_channel?.transfer_channel_id)
6785
- || cleanText(transferPayload.transfer_channel_id)
6786
- || cleanText(transferPayload.channel_id)
6787
- || null;
6788
-
6789
- if (reviewTransferNeeded && transferChannelId && transferPacketId) {
6790
- const watchSurface = typeof this.startMessageWatch === 'function'
6791
- ? this.startMessageWatch.bind(this)
6792
- : null;
6793
- if (!watchSurface) {
6794
- missingSurfaces.push('startMessageWatch');
6795
- } else {
6796
- watchResult = await watchSurface({
6797
- transferChannelId,
6798
- workTransferPacketId: transferPacketId,
6799
- workflowRunId: resolvedWorkflowRunId,
6800
- watchingAgentRole: normalizedSubmittedBy,
6801
- expectedFromRole: normalizedAssignedReviewer,
6802
- expectedMessageKind: 'response',
6803
- watchType: 'expected_peer_message',
6804
- watchingAgentSessionId: normalizedSubmittedBy,
6805
- expectedPayload: {
6806
- asset_id: normalizedAssetId,
6807
- classification_id: normalizedClassificationId,
6808
- candidate_count: candidateCount,
6809
- },
6810
- currentStatus: 'watching',
6811
- operatorNudge: normalizedRecommendedNextStep || normalizedClassificationCategory || normalizedSourceRef,
6812
- metadata: {
6813
- ...normalizedMetadata,
6814
- source: 'discoverSalvageCandidates',
6815
- },
6816
- });
6817
- }
6818
- }
6819
-
6820
- const watchPayload = isPlainObject(watchResult) ? watchResult : {};
6821
- const watchId = cleanText(watchPayload.message_watch_id)
6822
- || cleanText(watchPayload.watch_id)
6823
- || cleanText(watchPayload.message_watch?.message_watch_id)
6824
- || cleanText(watchPayload.message_watch?.watch_id)
6825
- || null;
6826
-
6827
- if ((discoveryObservation || transferResult) && typeof this.postCollaborationHeartbeat === 'function') {
6828
- try {
6829
- heartbeatResult = await this.postCollaborationHeartbeat({
6830
- transferChannelId: transferChannelId || undefined,
6831
- workTransferPacketId: transferPacketId || undefined,
6832
- workflowRunId: resolvedWorkflowRunId,
6833
- participantRole: reviewTransferNeeded ? 'salvage_reviewer' : 'salvage_discovery',
6834
- currentPhase: reviewTransferNeeded ? 'review_pending' : 'discovery_complete',
6835
- currentTaskSummary: normalizedCandidateSearchIntent || normalizedSourceRef || 'Salvage candidate discovery',
6836
- });
6837
- } catch (error) {
6838
- void error;
6839
- }
6840
- }
6841
-
6842
- const heartbeatPayload = isPlainObject(heartbeatResult) ? heartbeatResult : {};
6843
- const heartbeatStatus = cleanText(heartbeatPayload.activity_state)
6844
- || cleanText(heartbeatPayload.collaboration_heartbeat?.activity_state)
6845
- || cleanText(heartbeatPayload.status)
6846
- || cleanText(heartbeatPayload.heartbeat_status)
6847
- || null;
6848
-
6849
- return {
6850
- status: missingSurfaces.length > 0 ? 'partial' : 'ready',
6851
- project_id: resolvedProjectId || null,
6852
- asset_id: cleanText(assetIntakeRecord?.asset_id)
6853
- || cleanText(assetIntakeRecord?.assetId)
6854
- || normalizedAssetId
6855
- || null,
6856
- intake_record_id: cleanText(assetIntakeRecord?.intake_record_id)
6857
- || cleanText(assetIntakeRecord?.intakeRecordId)
6858
- || cleanText(intake_record_id)
6859
- || cleanText(intakeRecordId)
6860
- || null,
6861
- classification_id: cleanText(classificationRecord?.classification_id)
6862
- || cleanText(classificationRecord?.classificationId)
6863
- || normalizedClassificationId
6864
- || null,
6865
- candidate_count: candidateCount,
6866
- candidates,
6867
- review_required: Boolean(normalizedReviewRequired),
6868
- assigned_reviewer: normalizedAssignedReviewer || null,
6869
- transfer_packet_id: transferPacketId,
6870
- watch_id: watchId,
6871
- heartbeat_status: heartbeatStatus,
6872
- operator_projection_metadata: {
6873
- portfolio_bundle: portfolioBundle,
6874
- project_roadmap_summary: projectRoadmapSummary,
6875
- project_roadmap_active_item: projectActiveItem,
6876
- asset_intake_record: assetIntakeRecord,
6877
- classification_record: classificationRecord,
6878
- code_intent_search: codeIntentResult,
6879
- symbol_search: symbolSearchResult,
6880
- related_code: relatedCodeResults,
6881
- discovery_observation: discoveryObservation,
6882
- transfer: transferPayload,
6883
- message_watch: watchPayload,
6884
- heartbeat: heartbeatPayload,
6885
- },
6886
- missing_surfaces: [...new Set(missingSurfaces)],
6887
- project: projectPayload,
6888
- asset_intake_record: assetIntakeRecord,
6889
- classification_record: classificationRecord,
6890
- code_intent_search: codeIntentResult,
6891
- symbol_search: symbolSearchResult,
6892
- related_code: relatedCodeResults,
6893
- discovery_observation: discoveryObservation,
6894
- transfer: transferPayload,
6895
- message_watch: watchPayload,
6896
- heartbeat: heartbeatPayload,
6897
- };
6898
- }
6899
-
6900
- async createModernizationWorkPacket({
6901
- projectIdentifier,
6902
- projectId,
6903
- relatedProjectId,
6904
- related_project_id,
6905
- startWorkBody = {},
6906
- startWorkRequest = {},
6907
- resumeProjectWorkOptions = {},
6908
- asset = {},
6909
- modernizationAsset = {},
6910
- classification = {},
6911
- modernizationClassification = {},
6912
- assetId,
6913
- asset_id,
6914
- intakeRecordId,
6915
- intake_record_id,
6916
- classificationId,
6917
- classification_id,
6918
- selectedCandidates = [],
6919
- selected_candidates = [],
6920
- candidateIds = [],
6921
- candidate_ids = [],
6922
- candidateObjects = [],
6923
- candidate_objects = [],
6924
- sourceRef,
6925
- source_ref,
6926
- packetTitle,
6927
- packet_title,
6928
- problemStatement,
6929
- problem_statement,
6930
- recommendedModernization,
6931
- recommended_modernization,
6932
- affectedFilesOrSymbols = [],
6933
- affected_files_or_symbols = [],
6934
- acceptanceCriteria = [],
6935
- acceptance_criteria = [],
6936
- riskLevel,
6937
- risk_level,
6938
- evidenceRefs = [],
6939
- evidence_refs = [],
6940
- wrapperExecutionRequired,
6941
- wrapper_execution_required,
6942
- handoffNotes,
6943
- handoff_notes,
6944
- targetAgent,
6945
- target_agent,
6946
- assignedOwner,
6947
- assigned_owner,
6948
- collaborationRequired,
6949
- collaboration_required,
6950
- channelId,
6951
- channel_id,
6952
- transferChannelId,
6953
- transfer_channel_id,
6954
- submittedBy,
6955
- submitted_by,
6956
- includePortfolioBundle = true,
6957
- includeProjectRoadmap = true,
6958
- includeProjectActiveItem = true,
6959
- includeTransferChannelProjection = true,
6960
- metadata = {},
6961
- } = {}) {
6962
- const missingSurfaces = [];
6963
- const normalizedMetadata = isPlainObject(metadata) ? metadata : {};
6964
- const normalizedAsset = isPlainObject(modernizationAsset)
6965
- ? modernizationAsset
6966
- : (isPlainObject(asset) ? asset : {});
6967
- const normalizedClassification = isPlainObject(modernizationClassification)
6968
- ? modernizationClassification
6969
- : (isPlainObject(classification) ? classification : {});
6970
- const normalizedProjectReference = cleanText(projectIdentifier)
6971
- || cleanText(projectId)
6972
- || cleanText(relatedProjectId)
6973
- || cleanText(related_project_id)
6974
- || cleanText(normalizedAsset.related_project_id)
6975
- || cleanText(normalizedAsset.relatedProjectId)
6976
- || cleanText(normalizedClassification.related_project_id)
6977
- || cleanText(normalizedClassification.relatedProjectId);
6978
- const normalizedAssetId = cleanText(asset_id)
6979
- || cleanText(assetId)
6980
- || cleanText(intake_record_id)
6981
- || cleanText(intakeRecordId)
6982
- || cleanText(normalizedAsset.asset_id)
6983
- || cleanText(normalizedAsset.assetId)
6984
- || cleanText(normalizedAsset.intake_record_id)
6985
- || cleanText(normalizedAsset.intakeRecordId);
6986
- const normalizedClassificationId = cleanText(classification_id)
6987
- || cleanText(classificationId)
6988
- || cleanText(normalizedClassification.classification_id)
6989
- || cleanText(normalizedClassification.classificationId);
6990
- const normalizedSourceRef = cleanText(source_ref)
6991
- || cleanText(sourceRef)
6992
- || cleanText(normalizedAsset.source_ref)
6993
- || cleanText(normalizedAsset.sourceRef)
6994
- || normalizedAssetId;
6995
- const normalizedPacketTitle = cleanText(packet_title) || cleanText(packetTitle);
6996
- const normalizedProblemStatement = cleanText(problem_statement)
6997
- || cleanText(problemStatement)
6998
- || normalizedPacketTitle
6999
- || normalizedSourceRef
7000
- || 'Modernization work packet';
7001
- const normalizedRecommendedModernization = cleanText(recommended_modernization)
7002
- || cleanText(recommendedModernization)
7003
- || cleanText(normalizedClassification.recommended_modernization)
7004
- || cleanText(normalizedClassification.recommendedModernization);
7005
- const normalizedRiskLevel = cleanText(risk_level)
7006
- || cleanText(riskLevel)
7007
- || cleanText(normalizedClassification.risk_level)
7008
- || cleanText(normalizedClassification.riskLevel)
7009
- || null;
7010
- const normalizedHandoffNotes = cleanText(handoff_notes) || cleanText(handoffNotes);
7011
- const normalizedSubmittedBy = cleanText(submitted_by)
7012
- || cleanText(submittedBy)
7013
- || this.agentSessionId
7014
- || this.actorId
7015
- || null;
7016
- const normalizedTargetAgent = cleanText(target_agent) || cleanText(targetAgent) || cleanText(assigned_owner) || cleanText(assignedOwner) || null;
7017
- const normalizedWrapperExecutionRequired = wrapper_execution_required ?? wrapperExecutionRequired ?? true;
7018
- const normalizedCollaborationRequired = collaboration_required ?? collaborationRequired ?? Boolean(normalizedTargetAgent);
7019
- const normalizedAffectedFilesOrSymbols = cleanList(affected_files_or_symbols.length > 0 ? affected_files_or_symbols : affectedFilesOrSymbols);
7020
- const normalizedAcceptanceCriteria = cleanList(acceptance_criteria.length > 0 ? acceptance_criteria : acceptanceCriteria);
7021
- const normalizedEvidenceRefs = cleanList(evidence_refs.length > 0 ? evidence_refs : evidenceRefs);
7022
-
7023
- const candidateEntries = [
7024
- ...(Array.isArray(selected_candidates) && selected_candidates.length > 0 ? selected_candidates : selectedCandidates),
7025
- ...(Array.isArray(candidate_objects) && candidate_objects.length > 0 ? candidate_objects : candidateObjects),
7026
- ];
7027
- const normalizedCandidateIds = [];
7028
- const normalizedCandidateRecords = [];
7029
- for (const entry of candidateEntries) {
7030
- if (typeof entry === 'string') {
7031
- const candidateId = cleanText(entry);
7032
- if (candidateId) normalizedCandidateIds.push(candidateId);
7033
- continue;
7034
- }
7035
- if (!isPlainObject(entry)) continue;
7036
- const candidateId = cleanText(entry.candidate_id)
7037
- || cleanText(entry.candidateId)
7038
- || cleanText(entry.salvage_candidate_id)
7039
- || cleanText(entry.salvageCandidateId)
7040
- || cleanText(entry.work_packet_candidate_id)
7041
- || cleanText(entry.workPacketCandidateId)
7042
- || cleanText(entry.source_ref)
7043
- || cleanText(entry.sourceRef)
7044
- || cleanText(entry.qualified_name)
7045
- || cleanText(entry.qualifiedName);
7046
- if (candidateId) normalizedCandidateIds.push(candidateId);
7047
- normalizedCandidateRecords.push(entry);
7048
- }
7049
- for (const candidateId of Array.isArray(candidate_ids) && candidate_ids.length > 0 ? candidate_ids : candidateIds) {
7050
- const normalizedCandidateId = cleanText(candidateId);
7051
- if (normalizedCandidateId) normalizedCandidateIds.push(normalizedCandidateId);
7052
- }
7053
- const uniqueCandidateIds = [...new Set(normalizedCandidateIds)].filter(Boolean);
7054
- if (uniqueCandidateIds.length === 0) {
7055
- throw new Error('candidate_ids or selected_candidates is required.');
7056
- }
7057
-
7058
- let continuation = null;
7059
- let startWorkResult = null;
7060
- if (normalizedProjectReference) {
7061
- if (typeof this.resumeProjectWork === 'function') {
7062
- continuation = await this.resumeProjectWork({
7063
- projectIdentifier: normalizedProjectReference,
7064
- projectId: normalizedProjectReference,
7065
- requireClaim: false,
7066
- ...resumeProjectWorkOptions,
7067
- });
7068
- } else {
7069
- missingSurfaces.push('resumeProjectWork');
7070
- }
7071
- } else if (Object.keys(isPlainObject(startWorkRequest) ? startWorkRequest : {}).length > 0 || Object.keys(isPlainObject(startWorkBody) ? startWorkBody : {}).length > 0) {
7072
- if (typeof this.startWork === 'function') {
7073
- startWorkResult = await this.startWork({
7074
- ...(isPlainObject(startWorkBody) ? startWorkBody : {}),
7075
- ...(isPlainObject(startWorkRequest) ? startWorkRequest : {}),
7076
- });
7077
- } else {
7078
- missingSurfaces.push('startWork');
7079
- }
7080
- }
7081
-
7082
- const projectPayload = isPlainObject(continuation?.project)
7083
- ? continuation.project
7084
- : isPlainObject(startWorkResult?.project)
7085
- ? startWorkResult.project
7086
- : isPlainObject(continuation?.summary)
7087
- ? continuation.summary
7088
- : {};
7089
- const resolvedProjectId = cleanText(projectPayload.project_id)
7090
- || cleanText(projectPayload.projectId)
7091
- || normalizedProjectReference;
7092
- const resolvedWorkflowId = cleanText(projectPayload.workflow_id)
7093
- || cleanText(projectPayload.workflowId)
7094
- || cleanText(continuation?.workflow_id)
7095
- || cleanText(startWorkResult?.workflow_id);
7096
- const resolvedWorkflowRunId = cleanText(projectPayload.workflow_run_id)
7097
- || cleanText(projectPayload.workflowRunId)
7098
- || cleanText(continuation?.workflow_run_id)
7099
- || cleanText(startWorkResult?.workflow_run_id)
7100
- || cleanText(startWorkResult?.workflowRunId);
7101
-
7102
- const portfolioBundle = includePortfolioBundle && typeof this.getPortfolioBundle === 'function'
7103
- ? await this.getPortfolioBundle().catch(() => null)
7104
- : null;
7105
- const projectRoadmapSummary = resolvedProjectId && includeProjectRoadmap && typeof this.getProjectRoadmapSummary === 'function'
7106
- ? await this.getProjectRoadmapSummary(resolvedProjectId).catch(() => null)
7107
- : null;
7108
- const projectActiveItem = resolvedProjectId && includeProjectActiveItem && typeof this.getProjectRoadmapActiveItem === 'function'
7109
- ? await this.getProjectRoadmapActiveItem(resolvedProjectId).catch(() => null)
7110
- : null;
7111
-
7112
- let assetIntakeRecord = null;
7113
- const intakeLookupSurface = typeof this.getModernizationAssetIntakeRecord === 'function'
7114
- ? this.getModernizationAssetIntakeRecord.bind(this)
7115
- : null;
7116
- if (!intakeLookupSurface) {
7117
- missingSurfaces.push('getModernizationAssetIntakeRecord');
7118
- } else {
7119
- assetIntakeRecord = await intakeLookupSurface({
7120
- projectId: resolvedProjectId,
7121
- workflowId: resolvedWorkflowId,
7122
- workflowRunId: resolvedWorkflowRunId,
7123
- assetId: normalizedAssetId,
7124
- intakeRecordId: cleanText(intake_record_id) || cleanText(intakeRecordId),
7125
- sourceRef: normalizedSourceRef,
7126
- metadata: {
7127
- ...normalizedMetadata,
7128
- source: 'createModernizationWorkPacket',
7129
- },
7130
- });
7131
- }
7132
-
7133
- let classificationRecord = null;
7134
- const classificationLookupSurface = typeof this.getModernizationAssetClassificationRecord === 'function'
7135
- ? this.getModernizationAssetClassificationRecord.bind(this)
7136
- : null;
7137
- if (!classificationLookupSurface) {
7138
- missingSurfaces.push('getModernizationAssetClassificationRecord');
7139
- } else {
7140
- classificationRecord = await classificationLookupSurface({
7141
- projectId: resolvedProjectId,
7142
- workflowId: resolvedWorkflowId,
7143
- workflowRunId: resolvedWorkflowRunId,
7144
- assetId: normalizedAssetId,
7145
- classificationId: normalizedClassificationId,
7146
- sourceRef: normalizedSourceRef,
7147
- metadata: {
7148
- ...normalizedMetadata,
7149
- source: 'createModernizationWorkPacket',
7150
- },
7151
- });
7152
- }
7153
-
7154
- let candidateRecords = [];
7155
- const candidateLookupSurface = typeof this.getModernizationSalvageCandidateRecord === 'function'
7156
- ? this.getModernizationSalvageCandidateRecord.bind(this)
7157
- : typeof this.getModernizationWorkPacketCandidateRecord === 'function'
7158
- ? this.getModernizationWorkPacketCandidateRecord.bind(this)
7159
- : null;
7160
- if (!candidateLookupSurface) {
7161
- missingSurfaces.push('getModernizationSalvageCandidateRecord');
7162
- missingSurfaces.push('getModernizationWorkPacketCandidateRecord');
7163
- } else {
7164
- candidateRecords = await Promise.all(uniqueCandidateIds.map(async (candidateId) => {
7165
- try {
7166
- return await candidateLookupSurface({
7167
- projectId: resolvedProjectId,
7168
- workflowId: resolvedWorkflowId,
7169
- workflowRunId: resolvedWorkflowRunId,
7170
- candidateId,
7171
- sourceRef: normalizedSourceRef,
7172
- metadata: {
7173
- ...normalizedMetadata,
7174
- source: 'createModernizationWorkPacket',
7175
- },
7176
- });
7177
- } catch (error) {
7178
- void error;
7179
- return null;
7180
- }
7181
- }));
7182
- }
7183
-
7184
- let packetRecord = null;
7185
- const packetCreationSurface = typeof this.createModernizationWorkPacketRecord === 'function'
7186
- ? this.createModernizationWorkPacketRecord.bind(this)
7187
- : null;
7188
- if (!packetCreationSurface) {
7189
- missingSurfaces.push('createModernizationWorkPacketRecord');
7190
- } else {
7191
- packetRecord = await packetCreationSurface({
7192
- projectId: resolvedProjectId,
7193
- workflowId: resolvedWorkflowId,
7194
- workflowRunId: resolvedWorkflowRunId,
7195
- assetId: normalizedAssetId,
7196
- classificationId: normalizedClassificationId,
7197
- candidateIds: uniqueCandidateIds,
7198
- packetTitle: normalizedPacketTitle || normalizedProblemStatement,
7199
- sourceRef: normalizedSourceRef,
7200
- problemStatement: normalizedProblemStatement,
7201
- recommendedModernization: normalizedRecommendedModernization,
7202
- affectedFilesOrSymbols: normalizedAffectedFilesOrSymbols,
7203
- acceptanceCriteria: normalizedAcceptanceCriteria,
7204
- riskLevel: normalizedRiskLevel,
7205
- evidenceRefs: normalizedEvidenceRefs,
7206
- wrapperExecutionRequired: Boolean(normalizedWrapperExecutionRequired),
7207
- handoffNotes: normalizedHandoffNotes,
7208
- metadata: {
7209
- ...normalizedMetadata,
7210
- source: 'createModernizationWorkPacket',
7211
- },
7212
- });
7213
- }
7214
-
7215
- const transferSurface = typeof this.transferWorkPacket === 'function'
7216
- ? this.transferWorkPacket.bind(this)
7217
- : null;
7218
- if (!transferSurface) missingSurfaces.push('transferWorkPacket');
7219
- const transferResult = transferSurface
7220
- ? await transferSurface({
7221
- workflowRunId: resolvedWorkflowRunId,
7222
- transferKind: 'upstream_remediation',
7223
- objective: normalizedProblemStatement || normalizedRecommendedModernization || 'Modernization work packet',
7224
- requestedOutcome: normalizedRecommendedModernization || normalizedProblemStatement || 'Prepare modernization handoff.',
7225
- target: normalizedTargetAgent
7226
- ? {
7227
- intent: 'upstream_remediation',
7228
- recipient_mode: 'agent_session',
7229
- preferred_agent_session_id: normalizedTargetAgent,
7230
- preferred_role_key: normalizedTargetAgent,
7231
- }
7232
- : {
7233
- intent: 'upstream_remediation',
7234
- recipient_mode: 'role',
7235
- preferred_role_key: 'modernization_execution',
7236
- },
7237
- artifacts: normalizedAffectedFilesOrSymbols,
7238
- issues: [],
7239
- expectedEvidence: normalizedAcceptanceCriteria,
7240
- preferredModes: ['bundle', 'artifact_refs', 'inline_payload'],
7241
- capabilities: {
7242
- wrapper_execution_required: Boolean(normalizedWrapperExecutionRequired),
7243
- selected_candidate_count: uniqueCandidateIds.length,
7244
- },
7245
- senderAgentSessionId: normalizedSubmittedBy,
7246
- senderActorSessionId: null,
7247
- messageKind: 'handoff',
7248
- subject: normalizedPacketTitle || normalizedProblemStatement || 'Modernization work packet',
7249
- bodyMarkdown: normalizedHandoffNotes || normalizedRecommendedModernization || normalizedProblemStatement,
7250
- metadata: {
7251
- ...normalizedMetadata,
7252
- source: 'createModernizationWorkPacket',
7253
- asset_id: normalizedAssetId,
7254
- classification_id: normalizedClassificationId,
7255
- source_ref: normalizedSourceRef,
7256
- candidate_ids: uniqueCandidateIds,
7257
- selected_candidates: normalizedCandidateRecords,
7258
- wrapper_execution_required: Boolean(normalizedWrapperExecutionRequired),
7259
- },
7260
- })
7261
- : null;
7262
-
7263
- const transferPayload = isPlainObject(transferResult) ? transferResult : {};
7264
- let transferPacketId = cleanText(transferPayload.work_transfer_packet?.work_transfer_packet_id)
7265
- || cleanText(transferPayload.work_transfer_packet_id)
7266
- || cleanText(transferPayload.transfer_packet_id)
7267
- || cleanText(transferPayload.packet_id)
7268
- || null;
7269
-
7270
- const transferChannelInputId = cleanText(transfer_channel_id)
7271
- || cleanText(transferChannelId)
7272
- || cleanText(channel_id)
7273
- || cleanText(channelId);
7274
- const transferChannelSurface = transferChannelInputId
7275
- ? (typeof this.resumeTransferChannel === 'function'
7276
- ? this.resumeTransferChannel.bind(this)
7277
- : null)
7278
- : (transferPacketId && typeof this.openTransferChannel === 'function'
7279
- ? this.openTransferChannel.bind(this)
7280
- : null);
7281
- if (!transferChannelSurface && (transferChannelInputId || transferPacketId)) {
7282
- missingSurfaces.push(transferChannelInputId ? 'resumeTransferChannel' : 'openTransferChannel');
7283
- }
7284
- const channelResult = transferChannelSurface && transferPacketId
7285
- ? await transferChannelSurface({
7286
- transferChannelId: transferChannelInputId || undefined,
7287
- workTransferPacketId: transferPacketId,
7288
- workflowRunId: resolvedWorkflowRunId,
7289
- channelKind: 'bidirectional',
7290
- upstreamAgentSessionId: normalizedSubmittedBy,
7291
- downstreamAgentSessionId: normalizedTargetAgent || undefined,
7292
- evidenceRequiredForClosure: true,
7293
- metadata: {
7294
- ...normalizedMetadata,
7295
- source: 'createModernizationWorkPacket',
7296
- },
7297
- })
7298
- : null;
7299
- const channelPayload = isPlainObject(channelResult) ? channelResult : {};
7300
- const resolvedChannelId = cleanText(channelPayload.transfer_channel_id)
7301
- || cleanText(channelPayload.channel_id)
7302
- || cleanText(channelPayload.transfer_channel?.transfer_channel_id)
7303
- || cleanText(channelPayload.communication_transfer_channel?.transfer_channel_id)
7304
- || transferChannelInputId
7305
- || null;
7306
-
7307
- const ownershipSurface = normalizedTargetAgent && resolvedChannelId && typeof this.assignCollaborationOwnership === 'function'
7308
- ? this.assignCollaborationOwnership.bind(this)
7309
- : null;
7310
- if (normalizedTargetAgent && !ownershipSurface) missingSurfaces.push('assignCollaborationOwnership');
7311
- const ownershipResult = ownershipSurface
7312
- ? await ownershipSurface({
7313
- transferChannelId: resolvedChannelId,
7314
- workTransferPacketId: transferPacketId,
7315
- workflowRunId: resolvedWorkflowRunId,
7316
- participantRole: 'modernization_owner',
7317
- ownerAgentSessionId: normalizedTargetAgent,
7318
- ownerActorSessionId: null,
7319
- ownerLabel: normalizedTargetAgent,
7320
- assignmentState: 'assigned',
7321
- assignmentReason: normalizedProblemStatement || normalizedRecommendedModernization,
7322
- currentPhase: 'modernization_work_packet',
7323
- assignedByAgentSessionId: normalizedSubmittedBy,
7324
- assignedByActorSessionId: null,
7325
- metadata: {
7326
- ...normalizedMetadata,
7327
- source: 'createModernizationWorkPacket',
7328
- },
7329
- })
7330
- : null;
7331
- const ownershipPayload = isPlainObject(ownershipResult) ? ownershipResult : {};
7332
-
7333
- const proposalSurface = resolvedChannelId && typeof this.postCollaborationProposal === 'function'
7334
- ? this.postCollaborationProposal.bind(this)
7335
- : null;
7336
- if (resolvedChannelId && !proposalSurface) missingSurfaces.push('postCollaborationProposal');
7337
- const proposalResult = proposalSurface
7338
- ? await proposalSurface({
7339
- transferChannelId: resolvedChannelId,
7340
- workTransferPacketId: transferPacketId,
7341
- workflowRunId: resolvedWorkflowRunId,
7342
- proposalKind: 'modernization_work_packet',
7343
- proposalState: 'proposed',
7344
- participantRole: normalizedTargetAgent ? 'modernization_owner' : 'modernization_planner',
7345
- proposalSummary: normalizedProblemStatement || normalizedRecommendedModernization || 'Modernization work packet',
7346
- currentPhase: 'modernization_work_packet',
7347
- expectedNextUpdate: normalizedAcceptanceCriteria.join('; ') || normalizedRecommendedModernization || normalizedProblemStatement,
7348
- requiredEvidence: normalizedAcceptanceCriteria,
7349
- responseSchema: {
7350
- source_ref: normalizedSourceRef,
7351
- risk_level: normalizedRiskLevel,
7352
- candidate_ids: uniqueCandidateIds,
7353
- wrapper_execution_required: Boolean(normalizedWrapperExecutionRequired),
7354
- },
7355
- blockerSummary: normalizedProblemStatement || normalizedRecommendedModernization,
7356
- revisionNumber: 1,
7357
- proposerAgentSessionId: normalizedSubmittedBy,
7358
- proposerActorSessionId: null,
7359
- metadata: {
7360
- ...normalizedMetadata,
7361
- source: 'createModernizationWorkPacket',
7362
- },
7363
- })
7364
- : null;
7365
- const proposalPayload = isPlainObject(proposalResult) ? proposalResult : {};
7366
-
7367
- const watchSurface = resolvedChannelId && typeof this.startMessageWatch === 'function'
7368
- ? this.startMessageWatch.bind(this)
7369
- : null;
7370
- if (resolvedChannelId && !watchSurface) missingSurfaces.push('startMessageWatch');
7371
- const watchResult = watchSurface
7372
- ? await watchSurface({
7373
- transferChannelId: resolvedChannelId,
7374
- workTransferPacketId: transferPacketId,
7375
- workflowRunId: resolvedWorkflowRunId,
7376
- watchingAgentRole: normalizedSubmittedBy,
7377
- expectedFromRole: normalizedTargetAgent || 'modernization_owner',
7378
- expectedMessageKind: 'response',
7379
- watchType: 'expected_peer_message',
7380
- watchingAgentSessionId: normalizedSubmittedBy,
7381
- expectedPayload: {
7382
- source_ref: normalizedSourceRef,
7383
- candidate_ids: uniqueCandidateIds,
7384
- expected_acknowledgement: 'modernization work packet received',
7385
- },
7386
- currentStatus: 'watching',
7387
- operatorNudge: normalizedHandoffNotes || normalizedRecommendedModernization || normalizedProblemStatement,
7388
- metadata: {
7389
- ...normalizedMetadata,
7390
- source: 'createModernizationWorkPacket',
7391
- },
7392
- })
7393
- : null;
7394
- const watchPayload = isPlainObject(watchResult) ? watchResult : {};
7395
-
7396
- const heartbeatSurface = resolvedChannelId && typeof this.postCollaborationHeartbeat === 'function'
7397
- ? this.postCollaborationHeartbeat.bind(this)
7398
- : null;
7399
- if (resolvedChannelId && !heartbeatSurface) missingSurfaces.push('postCollaborationHeartbeat');
7400
- const heartbeatResult = heartbeatSurface
7401
- ? await heartbeatSurface({
7402
- transferChannelId: resolvedChannelId,
7403
- workTransferPacketId: transferPacketId,
7404
- workflowRunId: resolvedWorkflowRunId,
7405
- participantRole: normalizedTargetAgent ? 'modernization_owner' : 'modernization_planner',
7406
- currentPhase: 'modernization_work_packet',
7407
- currentTaskSummary: normalizedProblemStatement || normalizedRecommendedModernization || 'Modernization work packet',
7408
- metadata: {
7409
- ...normalizedMetadata,
7410
- source: 'createModernizationWorkPacket',
7411
- },
7412
- }).catch(() => null)
7413
- : null;
7414
- const heartbeatPayload = isPlainObject(heartbeatResult) ? heartbeatResult : {};
7415
-
7416
- const transferChannelProjectionSurface = includeTransferChannelProjection && resolvedChannelId
7417
- ? (typeof this.getTransferChannelProjection === 'function'
7418
- ? this.getTransferChannelProjection.bind(this)
7419
- : typeof this.getLogaTransferChannelThreadProjection === 'function'
7420
- ? this.getLogaTransferChannelThreadProjection.bind(this)
7421
- : null)
7422
- : null;
7423
- if (includeTransferChannelProjection && resolvedChannelId && !transferChannelProjectionSurface) missingSurfaces.push('getTransferChannelProjection');
7424
- const transferChannelProjection = transferChannelProjectionSurface && resolvedChannelId
7425
- ? await transferChannelProjectionSurface(resolvedChannelId).catch(() => null)
7426
- : null;
7427
-
7428
- const modernizationPacketId = cleanText(packetRecord?.modernization_packet_id)
7429
- || cleanText(packetRecord?.modernizationPacketId)
7430
- || cleanText(packetRecord?.work_packet_id)
7431
- || cleanText(packetRecord?.workPacketId)
7432
- || null;
7433
- const candidateCount = uniqueCandidateIds.length;
7434
- const proposalId = cleanText(proposalPayload.collaboration_proposal_id)
7435
- || cleanText(proposalPayload.proposal_id)
7436
- || cleanText(proposalPayload.collaboration_proposal?.collaboration_proposal_id)
7437
- || cleanText(proposalPayload.collaboration_proposal?.proposal_id)
7438
- || null;
7439
- const watchId = cleanText(watchPayload.message_watch_id)
7440
- || cleanText(watchPayload.watch_id)
7441
- || cleanText(watchPayload.message_watch?.message_watch_id)
7442
- || cleanText(watchPayload.message_watch?.watch_id)
7443
- || null;
7444
- const expectedAcknowledgement = cleanText(watchPayload.expected_message_kind)
7445
- || cleanText(watchPayload.message_watch?.expected_message_kind)
7446
- || cleanText(watchPayload.message_watch?.expected_acknowledgement)
7447
- || (normalizedTargetAgent ? 'acknowledgement' : null);
7448
- const assignedOwnerResolved = cleanText(ownershipPayload.owner_agent_session_id)
7449
- || cleanText(ownershipPayload.ownerAgentSessionId)
7450
- || normalizedTargetAgent
7451
- || null;
7452
- const heartbeatStatus = cleanText(heartbeatPayload.collaboration_heartbeat?.activity_state)
7453
- || cleanText(heartbeatPayload.activity_state)
7454
- || cleanText(heartbeatPayload.status)
7455
- || cleanText(heartbeatPayload.collaboration_heartbeat?.status)
7456
- || null;
7457
- const transferPacketResolvedId = transferPacketId
7458
- || cleanText(transferPayload.work_transfer_packet?.work_transfer_packet_id)
7459
- || cleanText(transferPayload.work_transfer_packet_id)
7460
- || cleanText(transferPayload.transfer_packet_id)
7461
- || cleanText(transferPayload.packet_id)
7462
- || null;
7463
-
7464
- return {
7465
- status: missingSurfaces.length > 0 ? 'partial' : 'ready',
7466
- project_id: resolvedProjectId || null,
7467
- asset_id: cleanText(assetIntakeRecord?.asset_id)
7468
- || cleanText(assetIntakeRecord?.assetId)
7469
- || normalizedAssetId
7470
- || null,
7471
- intake_record_id: cleanText(assetIntakeRecord?.intake_record_id)
7472
- || cleanText(assetIntakeRecord?.intakeRecordId)
7473
- || cleanText(intake_record_id)
7474
- || cleanText(intakeRecordId)
7475
- || null,
7476
- classification_id: cleanText(classificationRecord?.classification_id)
7477
- || cleanText(classificationRecord?.classificationId)
7478
- || normalizedClassificationId
7479
- || null,
7480
- candidate_ids: uniqueCandidateIds,
7481
- candidate_count: candidateCount,
7482
- modernization_packet_id: modernizationPacketId,
7483
- transfer_packet_id: transferPacketResolvedId,
7484
- channel_id: resolvedChannelId,
7485
- proposal_id: proposalId,
7486
- assigned_owner: assignedOwnerResolved,
7487
- watch_id: watchId,
7488
- expected_acknowledgement: expectedAcknowledgement,
7489
- heartbeat_status: heartbeatStatus,
7490
- operator_projection_metadata: {
7491
- portfolio_bundle: portfolioBundle,
7492
- project_roadmap_summary: projectRoadmapSummary,
7493
- project_roadmap_active_item: projectActiveItem,
7494
- asset_intake_record: assetIntakeRecord,
7495
- classification_record: classificationRecord,
7496
- candidate_records: candidateRecords,
7497
- modernization_packet_record: packetRecord,
7498
- transfer: transferPayload,
7499
- channel: channelPayload,
7500
- ownership_assignment: ownershipPayload,
7501
- proposal: proposalPayload,
7502
- message_watch: watchPayload,
7503
- heartbeat: heartbeatPayload,
7504
- transfer_channel_projection: transferChannelProjection,
7505
- },
7506
- missing_surfaces: [...new Set(missingSurfaces)],
7507
- project: projectPayload,
7508
- asset_intake_record: assetIntakeRecord,
7509
- classification_record: classificationRecord,
7510
- candidate_records: candidateRecords,
7511
- modernization_packet_record: packetRecord,
7512
- transfer: transferPayload,
7513
- channel: channelPayload,
7514
- ownership_assignment: ownershipPayload,
7515
- proposal: proposalPayload,
7516
- message_watch: watchPayload,
7517
- heartbeat: heartbeatPayload,
7518
- selected_candidates: normalizedCandidateRecords,
7519
- };
7520
- }
7521
-
7522
- async requestModernizationWrapperExecution({
7523
- projectIdentifier,
7524
- projectId,
7525
- relatedProjectId,
7526
- related_project_id,
7527
- startWorkBody = {},
7528
- startWorkRequest = {},
7529
- resumeProjectWorkOptions = {},
7530
- modernizationPacket = {},
7531
- modernization_packet = {},
7532
- modernizationPacketId,
7533
- modernization_packet_id,
7534
- packetId,
7535
- packet_id,
7536
- sourceRef,
7537
- source_ref,
7538
- wrapperName,
7539
- wrapper_name,
7540
- wrapperContractRef,
7541
- wrapper_contract_ref,
7542
- executionScope = [],
7543
- execution_scope = [],
7544
- allowedBlastRadius = [],
7545
- allowed_blast_radius = [],
7546
- requiredEvidence = [],
7547
- required_evidence = [],
7548
- acceptanceCriteria = [],
7549
- acceptance_criteria = [],
7550
- riskLevel,
7551
- risk_level,
7552
- targetAgent,
7553
- target_agent,
7554
- handoffNotes,
7555
- handoff_notes,
7556
- collaborationRequired,
7557
- collaboration_required,
7558
- channelId,
7559
- channel_id,
7560
- transferChannelId,
7561
- transfer_channel_id,
7562
- assignedOwner,
7563
- assigned_owner,
7564
- wrapperExecutionRequired,
7565
- wrapper_execution_required,
7566
- submittedBy,
7567
- submitted_by,
7568
- includePortfolioBundle = true,
7569
- includeProjectRoadmap = true,
7570
- includeProjectActiveItem = true,
7571
- includeTransferChannelProjection = true,
7572
- metadata = {},
7573
- } = {}) {
7574
- const missingSurfaces = [];
7575
- const normalizedMetadata = isPlainObject(metadata) ? metadata : {};
7576
- const normalizedPacket = isPlainObject(modernization_packet)
7577
- ? modernization_packet
7578
- : (isPlainObject(modernizationPacket) ? modernizationPacket : {});
7579
- const normalizedProjectReference = cleanText(projectIdentifier)
7580
- || cleanText(projectId)
7581
- || cleanText(relatedProjectId)
7582
- || cleanText(related_project_id)
7583
- || cleanText(normalizedPacket.project_id)
7584
- || cleanText(normalizedPacket.projectId)
7585
- || cleanText(normalizedPacket.related_project_id)
7586
- || cleanText(normalizedPacket.relatedProjectId);
7587
- const normalizedPacketId = cleanText(modernization_packet_id)
7588
- || cleanText(modernizationPacketId)
7589
- || cleanText(packet_id)
7590
- || cleanText(packetId)
7591
- || cleanText(normalizedPacket.modernization_packet_id)
7592
- || cleanText(normalizedPacket.modernizationPacketId)
7593
- || cleanText(normalizedPacket.packet_id)
7594
- || cleanText(normalizedPacket.packetId);
7595
- const normalizedSourceRef = cleanText(source_ref)
7596
- || cleanText(sourceRef)
7597
- || cleanText(normalizedPacket.source_ref)
7598
- || cleanText(normalizedPacket.sourceRef)
7599
- || normalizedPacketId;
7600
- const normalizedWrapperName = cleanText(wrapper_name) || cleanText(wrapperName);
7601
- const normalizedWrapperContractRef = cleanText(wrapper_contract_ref) || cleanText(wrapperContractRef);
7602
- const normalizedExecutionScope = cleanList(execution_scope.length > 0 ? execution_scope : executionScope);
7603
- const normalizedAllowedBlastRadius = cleanList(allowed_blast_radius.length > 0 ? allowed_blast_radius : allowedBlastRadius);
7604
- const normalizedRequiredEvidence = cleanList(required_evidence.length > 0 ? required_evidence : requiredEvidence);
7605
- const normalizedAcceptanceCriteria = cleanList(acceptance_criteria.length > 0 ? acceptance_criteria : acceptanceCriteria);
7606
- const normalizedRiskLevel = cleanText(risk_level)
7607
- || cleanText(riskLevel)
7608
- || cleanText(normalizedPacket.risk_level)
7609
- || cleanText(normalizedPacket.riskLevel)
7610
- || null;
7611
- const normalizedHandoffNotes = cleanText(handoff_notes) || cleanText(handoffNotes);
7612
- const normalizedSubmittedBy = cleanText(submitted_by)
7613
- || cleanText(submittedBy)
7614
- || this.agentSessionId
7615
- || this.actorId
7616
- || null;
7617
- const normalizedTargetAgent = cleanText(target_agent) || cleanText(targetAgent) || cleanText(assigned_owner) || cleanText(assignedOwner) || null;
7618
- const normalizedWrapperExecutionRequired = wrapper_execution_required ?? wrapperExecutionRequired ?? true;
7619
- const normalizedCollaborationRequired = collaboration_required ?? collaborationRequired ?? Boolean(normalizedTargetAgent || channelId || channel_id || transferChannelId || transfer_channel_id);
7620
-
7621
- let continuation = null;
7622
- let startWorkResult = null;
7623
- if (normalizedProjectReference) {
7624
- if (typeof this.resumeProjectWork === 'function') {
7625
- continuation = await this.resumeProjectWork({
7626
- projectIdentifier: normalizedProjectReference,
7627
- projectId: normalizedProjectReference,
7628
- requireClaim: false,
7629
- ...resumeProjectWorkOptions,
7630
- });
7631
- } else {
7632
- missingSurfaces.push('resumeProjectWork');
7633
- }
7634
- } else if (Object.keys(isPlainObject(startWorkRequest) ? startWorkRequest : {}).length > 0 || Object.keys(isPlainObject(startWorkBody) ? startWorkBody : {}).length > 0) {
7635
- if (typeof this.startWork === 'function') {
7636
- startWorkResult = await this.startWork({
7637
- ...(isPlainObject(startWorkBody) ? startWorkBody : {}),
7638
- ...(isPlainObject(startWorkRequest) ? startWorkRequest : {}),
7639
- });
7640
- } else {
7641
- missingSurfaces.push('startWork');
7642
- }
7643
- }
7644
-
7645
- const projectPayload = isPlainObject(continuation?.project)
7646
- ? continuation.project
7647
- : isPlainObject(startWorkResult?.project)
7648
- ? startWorkResult.project
7649
- : isPlainObject(continuation?.summary)
7650
- ? continuation.summary
7651
- : {};
7652
- const resolvedProjectId = cleanText(projectPayload.project_id)
7653
- || cleanText(projectPayload.projectId)
7654
- || normalizedProjectReference;
7655
- const resolvedWorkflowId = cleanText(projectPayload.workflow_id)
7656
- || cleanText(projectPayload.workflowId)
7657
- || cleanText(continuation?.workflow_id)
7658
- || cleanText(startWorkResult?.workflow_id);
7659
- const resolvedWorkflowRunId = cleanText(projectPayload.workflow_run_id)
7660
- || cleanText(projectPayload.workflowRunId)
7661
- || cleanText(continuation?.workflow_run_id)
7662
- || cleanText(startWorkResult?.workflow_run_id)
7663
- || cleanText(startWorkResult?.workflowRunId);
7664
-
7665
- const portfolioBundle = includePortfolioBundle && typeof this.getPortfolioBundle === 'function'
7666
- ? await this.getPortfolioBundle().catch(() => null)
7667
- : null;
7668
- const projectRoadmapSummary = resolvedProjectId && includeProjectRoadmap && typeof this.getProjectRoadmapSummary === 'function'
7669
- ? await this.getProjectRoadmapSummary(resolvedProjectId).catch(() => null)
7670
- : null;
7671
- const projectActiveItem = resolvedProjectId && includeProjectActiveItem && typeof this.getProjectRoadmapActiveItem === 'function'
7672
- ? await this.getProjectRoadmapActiveItem(resolvedProjectId).catch(() => null)
7673
- : null;
7674
-
7675
- let packetRecord = null;
7676
- const packetLookupSurface = typeof this.getModernizationWorkPacketRecord === 'function'
7677
- ? this.getModernizationWorkPacketRecord.bind(this)
7678
- : typeof this.getModernizationPacketRecord === 'function'
7679
- ? this.getModernizationPacketRecord.bind(this)
7680
- : null;
7681
- if (!packetLookupSurface) {
7682
- missingSurfaces.push('getModernizationWorkPacketRecord');
7683
- missingSurfaces.push('getModernizationPacketRecord');
7684
- } else {
7685
- packetRecord = await packetLookupSurface({
7686
- projectId: resolvedProjectId,
7687
- workflowId: resolvedWorkflowId,
7688
- workflowRunId: resolvedWorkflowRunId,
7689
- modernizationPacketId: normalizedPacketId,
7690
- packetId: normalizedPacketId,
7691
- sourceRef: normalizedSourceRef,
7692
- metadata: {
7693
- ...normalizedMetadata,
7694
- source: 'requestModernizationWrapperExecution',
7695
- },
7696
- }).catch(() => null);
7697
- }
7698
-
7699
- const packetRecordPayload = isPlainObject(packetRecord) ? packetRecord : {};
7700
- const resolvedModernizationPacketId = cleanText(packetRecordPayload.modernization_packet_id)
7701
- || cleanText(packetRecordPayload.modernizationPacketId)
7702
- || cleanText(packetRecordPayload.work_packet_id)
7703
- || cleanText(packetRecordPayload.workPacketId)
7704
- || normalizedPacketId
7705
- || null;
7706
-
7707
- let wrapperRequestRecord = null;
7708
- const wrapperRequestSurface = typeof this.createModernizationWrapperExecutionRequestRecord === 'function'
7709
- ? this.createModernizationWrapperExecutionRequestRecord.bind(this)
7710
- : typeof this.submitModernizationWrapperExecutionRequest === 'function'
7711
- ? this.submitModernizationWrapperExecutionRequest.bind(this)
7712
- : null;
7713
- if (!wrapperRequestSurface) {
7714
- missingSurfaces.push('createModernizationWrapperExecutionRequestRecord');
7715
- missingSurfaces.push('submitModernizationWrapperExecutionRequest');
7716
- } else {
7717
- wrapperRequestRecord = await wrapperRequestSurface({
7718
- projectId: resolvedProjectId,
7719
- workflowId: resolvedWorkflowId,
7720
- workflowRunId: resolvedWorkflowRunId,
7721
- modernizationPacketId: resolvedModernizationPacketId,
7722
- wrapperName: normalizedWrapperName,
7723
- wrapperContractRef: normalizedWrapperContractRef,
7724
- sourceRef: normalizedSourceRef,
7725
- executionScope: normalizedExecutionScope,
7726
- allowedBlastRadius: normalizedAllowedBlastRadius,
7727
- requiredEvidence: normalizedRequiredEvidence,
7728
- acceptanceCriteria: normalizedAcceptanceCriteria,
7729
- riskLevel: normalizedRiskLevel,
7730
- targetAgent: normalizedTargetAgent,
7731
- handoffNotes: normalizedHandoffNotes,
7732
- wrapperExecutionRequired: Boolean(normalizedWrapperExecutionRequired),
7733
- metadata: {
7734
- ...normalizedMetadata,
7735
- source: 'requestModernizationWrapperExecution',
7736
- },
7737
- }).catch(() => null);
7738
- }
7739
-
7740
- let transferResult = null;
7741
- const transferSurface = typeof this.transferWorkPacket === 'function'
7742
- ? this.transferWorkPacket.bind(this)
7743
- : null;
7744
- if (!transferSurface) missingSurfaces.push('transferWorkPacket');
7745
- if (transferSurface && (!wrapperRequestRecord || !wrapperRequestRecord.wrapper_execution_request_id) && (normalizedWrapperExecutionRequired || normalizedTargetAgent || normalizedCollaborationRequired)) {
7746
- transferResult = await transferSurface({
7747
- workflowRunId: resolvedWorkflowRunId,
7748
- transferKind: 'upstream_remediation',
7749
- objective: normalizedWrapperName || normalizedWrapperContractRef || 'Wrapper execution request',
7750
- requestedOutcome: normalizedHandoffNotes || normalizedWrapperContractRef || 'Prepare wrapper execution request.',
7751
- target: normalizedTargetAgent
7752
- ? {
7753
- intent: 'upstream_remediation',
7754
- recipient_mode: 'agent_session',
7755
- preferred_agent_session_id: normalizedTargetAgent,
7756
- preferred_role_key: normalizedTargetAgent,
7757
- }
7758
- : {
7759
- intent: 'upstream_remediation',
7760
- recipient_mode: 'role',
7761
- preferred_role_key: 'wrapper_execution',
7762
- },
7763
- artifacts: normalizedExecutionScope,
7764
- issues: normalizedAllowedBlastRadius,
7765
- expectedEvidence: normalizedRequiredEvidence,
7766
- preferredModes: ['bundle', 'artifact_refs', 'inline_payload'],
7767
- capabilities: {
7768
- wrapper_execution_required: Boolean(normalizedWrapperExecutionRequired),
7769
- allowed_blast_radius: normalizedAllowedBlastRadius,
7770
- },
7771
- senderAgentSessionId: normalizedSubmittedBy,
7772
- senderActorSessionId: null,
7773
- messageKind: 'handoff',
7774
- subject: normalizedWrapperName || normalizedWrapperContractRef || 'Wrapper execution request',
7775
- bodyMarkdown: normalizedHandoffNotes || normalizedWrapperContractRef || normalizedWrapperName,
7776
- metadata: {
7777
- ...normalizedMetadata,
7778
- source: 'requestModernizationWrapperExecution',
7779
- modernization_packet_id: resolvedModernizationPacketId,
7780
- wrapper_execution_required: Boolean(normalizedWrapperExecutionRequired),
7781
- },
7782
- });
7783
- }
7784
- const transferPayload = isPlainObject(transferResult) ? transferResult : {};
7785
-
7786
- const transferPacketId = cleanText(wrapperRequestRecord?.transfer_packet_id)
7787
- || cleanText(wrapperRequestRecord?.work_transfer_packet_id)
7788
- || cleanText(wrapperRequestRecord?.workTransferPacketId)
7789
- || cleanText(transferPayload.work_transfer_packet?.work_transfer_packet_id)
7790
- || cleanText(transferPayload.work_transfer_packet_id)
7791
- || cleanText(transferPayload.transfer_packet_id)
7792
- || cleanText(transferPayload.packet_id)
7793
- || null;
7794
-
7795
- const transferChannelInputId = cleanText(transfer_channel_id)
7796
- || cleanText(transferChannelId)
7797
- || cleanText(channel_id)
7798
- || cleanText(channelId);
7799
- const channelSurface = transferChannelInputId
7800
- ? (typeof this.resumeTransferChannel === 'function'
7801
- ? this.resumeTransferChannel.bind(this)
7802
- : null)
7803
- : (transferPacketId && typeof this.openTransferChannel === 'function'
7804
- ? this.openTransferChannel.bind(this)
7805
- : null);
7806
- if (!channelSurface && (transferChannelInputId || transferPacketId)) {
7807
- missingSurfaces.push(transferChannelInputId ? 'resumeTransferChannel' : 'openTransferChannel');
7808
- }
7809
- const channelResult = channelSurface && transferPacketId
7810
- ? await channelSurface({
7811
- transferChannelId: transferChannelInputId || undefined,
7812
- workTransferPacketId: transferPacketId,
7813
- workflowRunId: resolvedWorkflowRunId,
7814
- channelKind: 'bidirectional',
7815
- upstreamAgentSessionId: normalizedSubmittedBy,
7816
- downstreamAgentSessionId: normalizedTargetAgent || undefined,
7817
- evidenceRequiredForClosure: true,
7818
- metadata: {
7819
- ...normalizedMetadata,
7820
- source: 'requestModernizationWrapperExecution',
7821
- },
7822
- })
7823
- : null;
7824
- const channelPayload = isPlainObject(channelResult) ? channelResult : {};
7825
- const resolvedChannelId = cleanText(channelPayload.transfer_channel_id)
7826
- || cleanText(channelPayload.channel_id)
7827
- || cleanText(channelPayload.transfer_channel?.transfer_channel_id)
7828
- || cleanText(channelPayload.communication_transfer_channel?.transfer_channel_id)
7829
- || transferChannelInputId
7830
- || null;
7831
-
7832
- const ownershipSurface = normalizedTargetAgent && resolvedChannelId && typeof this.assignCollaborationOwnership === 'function'
7833
- ? this.assignCollaborationOwnership.bind(this)
7834
- : null;
7835
- if (normalizedTargetAgent && !ownershipSurface) missingSurfaces.push('assignCollaborationOwnership');
7836
- const ownershipResult = ownershipSurface
7837
- ? await ownershipSurface({
7838
- transferChannelId: resolvedChannelId,
7839
- workTransferPacketId: transferPacketId,
7840
- workflowRunId: resolvedWorkflowRunId,
7841
- participantRole: 'wrapper_executor',
7842
- ownerAgentSessionId: normalizedTargetAgent,
7843
- ownerActorSessionId: null,
7844
- ownerLabel: normalizedTargetAgent,
7845
- assignmentState: 'assigned',
7846
- assignmentReason: normalizedWrapperName || normalizedWrapperContractRef || normalizedHandoffNotes,
7847
- currentPhase: 'wrapper_execution_request',
7848
- assignedByAgentSessionId: normalizedSubmittedBy,
7849
- assignedByActorSessionId: null,
7850
- metadata: {
7851
- ...normalizedMetadata,
7852
- source: 'requestModernizationWrapperExecution',
7853
- },
7854
- })
7855
- : null;
7856
- const ownershipPayload = isPlainObject(ownershipResult) ? ownershipResult : {};
7857
-
7858
- const watchSurface = resolvedChannelId && typeof this.startMessageWatch === 'function'
7859
- ? this.startMessageWatch.bind(this)
7860
- : null;
7861
- if (resolvedChannelId && !watchSurface) missingSurfaces.push('startMessageWatch');
7862
- const watchResult = watchSurface
7863
- ? await watchSurface({
7864
- transferChannelId: resolvedChannelId,
7865
- workTransferPacketId: transferPacketId,
7866
- workflowRunId: resolvedWorkflowRunId,
7867
- watchingAgentRole: normalizedSubmittedBy,
7868
- expectedFromRole: normalizedTargetAgent || 'wrapper_executor',
7869
- expectedMessageKind: 'response',
7870
- watchType: 'expected_peer_message',
7871
- watchingAgentSessionId: normalizedSubmittedBy,
7872
- expectedPayload: {
7873
- modernization_packet_id: resolvedModernizationPacketId,
7874
- expected_acknowledgement: 'wrapper execution request received',
7875
- },
7876
- currentStatus: 'watching',
7877
- operatorNudge: normalizedHandoffNotes || normalizedWrapperContractRef || normalizedWrapperName,
7878
- metadata: {
7879
- ...normalizedMetadata,
7880
- source: 'requestModernizationWrapperExecution',
7881
- },
7882
- })
7883
- : null;
7884
- const watchPayload = isPlainObject(watchResult) ? watchResult : {};
7885
-
7886
- const heartbeatSurface = resolvedChannelId && typeof this.postCollaborationHeartbeat === 'function'
7887
- ? this.postCollaborationHeartbeat.bind(this)
7888
- : null;
7889
- if (resolvedChannelId && !heartbeatSurface) missingSurfaces.push('postCollaborationHeartbeat');
7890
- const heartbeatResult = heartbeatSurface
7891
- ? await heartbeatSurface({
7892
- transferChannelId: resolvedChannelId,
7893
- workTransferPacketId: transferPacketId,
7894
- workflowRunId: resolvedWorkflowRunId,
7895
- participantRole: normalizedTargetAgent ? 'wrapper_executor' : 'wrapper_planner',
7896
- currentPhase: 'wrapper_execution_request',
7897
- currentTaskSummary: normalizedWrapperName || normalizedWrapperContractRef || 'Wrapper execution request',
7898
- metadata: {
7899
- ...normalizedMetadata,
7900
- source: 'requestModernizationWrapperExecution',
7901
- },
7902
- }).catch(() => null)
7903
- : null;
7904
- const heartbeatPayload = isPlainObject(heartbeatResult) ? heartbeatResult : {};
7905
-
7906
- const transferChannelProjectionSurface = includeTransferChannelProjection && resolvedChannelId
7907
- ? (typeof this.getTransferChannelProjection === 'function'
7908
- ? this.getTransferChannelProjection.bind(this)
7909
- : typeof this.getLogaTransferChannelThreadProjection === 'function'
7910
- ? this.getLogaTransferChannelThreadProjection.bind(this)
7911
- : null)
7912
- : null;
7913
- if (includeTransferChannelProjection && resolvedChannelId && !transferChannelProjectionSurface) missingSurfaces.push('getTransferChannelProjection');
7914
- const transferChannelProjection = transferChannelProjectionSurface && resolvedChannelId
7915
- ? await transferChannelProjectionSurface(resolvedChannelId).catch(() => null)
7916
- : null;
7917
-
7918
- const wrapperExecutionRequestId = cleanText(wrapperRequestRecord?.wrapper_execution_request_id)
7919
- || cleanText(wrapperRequestRecord?.wrapperExecutionRequestId)
7920
- || cleanText(wrapperRequestRecord?.execution_request_id)
7921
- || cleanText(wrapperRequestRecord?.executionRequestId)
7922
- || null;
7923
- const expectedAcknowledgement = cleanText(watchPayload.expected_message_kind)
7924
- || cleanText(watchPayload.message_watch?.expected_message_kind)
7925
- || cleanText(watchPayload.message_watch?.expected_acknowledgement)
7926
- || (normalizedTargetAgent ? 'acknowledgement' : null);
7927
- const ownershipAssignmentId = cleanText(ownershipPayload.collaboration_ownership_assignment_id)
7928
- || cleanText(ownershipPayload.ownership_assignment_id)
7929
- || cleanText(ownershipPayload.collaboration_ownership_assignment?.collaboration_ownership_assignment_id)
7930
- || cleanText(ownershipPayload.collaboration_ownership_assignment?.ownership_assignment_id)
7931
- || null;
7932
- const watchId = cleanText(watchPayload.message_watch_id)
7933
- || cleanText(watchPayload.watch_id)
7934
- || cleanText(watchPayload.message_watch?.message_watch_id)
7935
- || cleanText(watchPayload.message_watch?.watch_id)
7936
- || null;
7937
- const heartbeatStatus = cleanText(heartbeatPayload.collaboration_heartbeat?.activity_state)
7938
- || cleanText(heartbeatPayload.activity_state)
7939
- || cleanText(heartbeatPayload.status)
7940
- || cleanText(heartbeatPayload.collaboration_heartbeat?.status)
7941
- || null;
7942
- const wrapperExecutionRequiredResolved = Boolean(normalizedWrapperExecutionRequired);
7943
-
7944
- return {
7945
- status: missingSurfaces.length > 0 ? 'partial' : 'ready',
7946
- project_id: resolvedProjectId || null,
7947
- modernization_packet_id: resolvedModernizationPacketId,
7948
- wrapper_execution_request_id: wrapperExecutionRequestId || null,
7949
- transfer_packet_id: transferPacketId,
7950
- target_agent: normalizedTargetAgent || null,
7951
- channel_id: resolvedChannelId,
7952
- ownership_assignment_id: ownershipAssignmentId,
7953
- watch_id: watchId,
7954
- expected_acknowledgement: expectedAcknowledgement,
7955
- wrapper_execution_required: wrapperExecutionRequiredResolved,
7956
- heartbeat_status: heartbeatStatus,
7957
- operator_projection_metadata: {
7958
- portfolio_bundle: portfolioBundle,
7959
- project_roadmap_summary: projectRoadmapSummary,
7960
- project_roadmap_active_item: projectActiveItem,
7961
- modernization_packet_record: packetRecordPayload,
7962
- wrapper_execution_request_record: wrapperRequestRecord,
7963
- transfer: transferPayload,
7964
- channel: channelPayload,
7965
- ownership_assignment: ownershipPayload,
7966
- message_watch: watchPayload,
7967
- heartbeat: heartbeatPayload,
7968
- transfer_channel_projection: transferChannelProjection,
7969
- },
7970
- missing_surfaces: [...new Set(missingSurfaces)],
7971
- project: projectPayload,
7972
- modernization_packet_record: packetRecordPayload,
7973
- wrapper_execution_request_record: wrapperRequestRecord,
7974
- transfer: transferPayload,
7975
- channel: channelPayload,
7976
- ownership_assignment: ownershipPayload,
7977
- message_watch: watchPayload,
7978
- heartbeat: heartbeatPayload,
7979
- };
7980
- }
7981
-
7982
- async getModernizationWrapperEvidence({
7983
- projectIdentifier,
7984
- projectId,
7985
- relatedProjectId,
7986
- related_project_id,
7987
- startWorkBody = {},
7988
- startWorkRequest = {},
7989
- resumeProjectWorkOptions = {},
7990
- modernizationPacket = {},
7991
- modernization_packet = {},
7992
- modernizationPacketId,
7993
- modernization_packet_id,
7994
- packetId,
7995
- packet_id,
7996
- wrapperExecutionRequest = {},
7997
- wrapper_execution_request = {},
7998
- wrapperExecutionRequestId,
7999
- wrapper_execution_request_id,
8000
- wrapperExecutionId,
8001
- wrapper_execution_id,
8002
- sourceRef,
8003
- source_ref,
8004
- includeOperations = true,
8005
- include_operations = true,
8006
- includeFileManifest = true,
8007
- include_file_manifest = true,
8008
- includeVerificationSummary = true,
8009
- include_verification_summary = true,
8010
- includeProjection = true,
8011
- include_projection = true,
8012
- includePortfolioBundle = true,
8013
- includeProjectRoadmap = true,
8014
- includeProjectActiveItem = true,
8015
- metadata = {},
8016
- } = {}) {
8017
- const missingSurfaces = [];
8018
- const normalizedMetadata = isPlainObject(metadata) ? metadata : {};
8019
- const normalizedPacket = isPlainObject(modernization_packet)
8020
- ? modernization_packet
8021
- : (isPlainObject(modernizationPacket) ? modernizationPacket : {});
8022
- const normalizedWrapperExecutionRequest = isPlainObject(wrapper_execution_request)
8023
- ? wrapper_execution_request
8024
- : (isPlainObject(wrapperExecutionRequest) ? wrapperExecutionRequest : {});
8025
- const normalizedProjectReference = cleanText(projectIdentifier)
8026
- || cleanText(projectId)
8027
- || cleanText(relatedProjectId)
8028
- || cleanText(related_project_id)
8029
- || cleanText(normalizedPacket.project_id)
8030
- || cleanText(normalizedPacket.projectId)
8031
- || cleanText(normalizedPacket.related_project_id)
8032
- || cleanText(normalizedPacket.relatedProjectId)
8033
- || cleanText(normalizedWrapperExecutionRequest.project_id)
8034
- || cleanText(normalizedWrapperExecutionRequest.projectId)
8035
- || cleanText(normalizedWrapperExecutionRequest.related_project_id)
8036
- || cleanText(normalizedWrapperExecutionRequest.relatedProjectId);
8037
- const normalizedModernizationPacketId = cleanText(modernization_packet_id)
8038
- || cleanText(modernizationPacketId)
8039
- || cleanText(packet_id)
8040
- || cleanText(packetId)
8041
- || cleanText(normalizedPacket.modernization_packet_id)
8042
- || cleanText(normalizedPacket.modernizationPacketId)
8043
- || cleanText(normalizedPacket.packet_id)
8044
- || cleanText(normalizedPacket.packetId)
8045
- || cleanText(normalizedWrapperExecutionRequest.modernization_packet_id)
8046
- || cleanText(normalizedWrapperExecutionRequest.modernizationPacketId)
8047
- || cleanText(normalizedWrapperExecutionRequest.packet_id)
8048
- || cleanText(normalizedWrapperExecutionRequest.packetId);
8049
- const normalizedWrapperExecutionRequestId = cleanText(wrapper_execution_request_id)
8050
- || cleanText(wrapperExecutionRequestId)
8051
- || cleanText(normalizedWrapperExecutionRequest.wrapper_execution_request_id)
8052
- || cleanText(normalizedWrapperExecutionRequest.wrapperExecutionRequestId)
8053
- || cleanText(normalizedWrapperExecutionRequest.execution_request_id)
8054
- || cleanText(normalizedWrapperExecutionRequest.executionRequestId);
8055
- const normalizedWrapperExecutionId = cleanText(wrapper_execution_id)
8056
- || cleanText(wrapperExecutionId)
8057
- || cleanText(normalizedWrapperExecutionRequest.wrapper_execution_id)
8058
- || cleanText(normalizedWrapperExecutionRequest.wrapperExecutionId)
8059
- || cleanText(normalizedWrapperExecutionRequest.execution_id)
8060
- || cleanText(normalizedWrapperExecutionRequest.executionId)
8061
- || cleanText(normalizedWrapperExecutionRequest.wrapper_execution_record_id)
8062
- || cleanText(normalizedWrapperExecutionRequest.wrapperExecutionRecordId);
8063
- const normalizedSourceRef = cleanText(source_ref)
8064
- || cleanText(sourceRef)
8065
- || cleanText(normalizedPacket.source_ref)
8066
- || cleanText(normalizedPacket.sourceRef)
8067
- || cleanText(normalizedWrapperExecutionRequest.source_ref)
8068
- || cleanText(normalizedWrapperExecutionRequest.sourceRef)
8069
- || normalizedModernizationPacketId
8070
- || normalizedWrapperExecutionRequestId
8071
- || normalizedWrapperExecutionId;
8072
- const normalizedIncludeOperations = include_operations ?? includeOperations ?? true;
8073
- const normalizedIncludeFileManifest = include_file_manifest ?? includeFileManifest ?? true;
8074
- const normalizedIncludeVerificationSummary = include_verification_summary ?? includeVerificationSummary ?? true;
8075
- const normalizedIncludeProjection = include_projection ?? includeProjection ?? true;
8076
-
8077
- const pickSurface = (candidateNames) => {
8078
- for (const candidateName of candidateNames) {
8079
- if (typeof this[candidateName] === 'function') {
8080
- return this[candidateName].bind(this);
8081
- }
8082
- }
8083
- for (const candidateName of candidateNames) {
8084
- missingSurfaces.push(candidateName);
8085
- }
8086
- return null;
8087
- };
8088
-
8089
- let continuation = null;
8090
- let startWorkResult = null;
8091
- if (normalizedProjectReference) {
8092
- if (typeof this.resumeProjectWork === 'function') {
8093
- continuation = await this.resumeProjectWork({
8094
- projectIdentifier: normalizedProjectReference,
8095
- projectId: normalizedProjectReference,
8096
- requireClaim: false,
8097
- ...resumeProjectWorkOptions,
8098
- });
8099
- } else {
8100
- missingSurfaces.push('resumeProjectWork');
8101
- }
8102
- } else if (Object.keys(isPlainObject(startWorkRequest) ? startWorkRequest : {}).length > 0 || Object.keys(isPlainObject(startWorkBody) ? startWorkBody : {}).length > 0) {
8103
- if (typeof this.startWork === 'function') {
8104
- startWorkResult = await this.startWork({
8105
- ...(isPlainObject(startWorkBody) ? startWorkBody : {}),
8106
- ...(isPlainObject(startWorkRequest) ? startWorkRequest : {}),
8107
- });
8108
- } else {
8109
- missingSurfaces.push('startWork');
8110
- }
8111
- }
8112
-
8113
- const projectPayload = isPlainObject(continuation?.project)
8114
- ? continuation.project
8115
- : isPlainObject(startWorkResult?.project)
8116
- ? startWorkResult.project
8117
- : isPlainObject(continuation?.summary)
8118
- ? continuation.summary
8119
- : {};
8120
- const resolvedProjectId = cleanText(projectPayload.project_id)
8121
- || cleanText(projectPayload.projectId)
8122
- || normalizedProjectReference;
8123
- const resolvedWorkflowId = cleanText(projectPayload.workflow_id)
8124
- || cleanText(projectPayload.workflowId)
8125
- || cleanText(continuation?.workflow_id)
8126
- || cleanText(startWorkResult?.workflow_id);
8127
- const resolvedWorkflowRunId = cleanText(projectPayload.workflow_run_id)
8128
- || cleanText(projectPayload.workflowRunId)
8129
- || cleanText(continuation?.workflow_run_id)
8130
- || cleanText(startWorkResult?.workflow_run_id)
8131
- || cleanText(startWorkResult?.workflowRunId);
8132
-
8133
- const portfolioBundle = includePortfolioBundle && typeof this.getPortfolioBundle === 'function'
8134
- ? await this.getPortfolioBundle().catch(() => null)
8135
- : null;
8136
- const projectRoadmapSummary = resolvedProjectId && includeProjectRoadmap && typeof this.getProjectRoadmapSummary === 'function'
8137
- ? await this.getProjectRoadmapSummary(resolvedProjectId).catch(() => null)
8138
- : null;
8139
- const projectActiveItem = resolvedProjectId && includeProjectActiveItem && typeof this.getProjectRoadmapActiveItem === 'function'
8140
- ? await this.getProjectRoadmapActiveItem(resolvedProjectId).catch(() => null)
8141
- : null;
8142
-
8143
- let modernizationPacketRecord = null;
8144
- const packetLookupSurface = pickSurface(['getModernizationWorkPacketRecord', 'getModernizationPacketRecord']);
8145
- if (packetLookupSurface) {
8146
- modernizationPacketRecord = await packetLookupSurface({
8147
- projectId: resolvedProjectId,
8148
- workflowId: resolvedWorkflowId,
8149
- workflowRunId: resolvedWorkflowRunId,
8150
- modernizationPacketId: normalizedModernizationPacketId,
8151
- packetId: normalizedModernizationPacketId,
8152
- sourceRef: normalizedSourceRef,
8153
- metadata: {
8154
- ...normalizedMetadata,
8155
- source: 'getModernizationWrapperEvidence',
8156
- },
8157
- }).catch(() => null);
8158
- }
8159
-
8160
- const packetRecordPayload = isPlainObject(modernizationPacketRecord) ? modernizationPacketRecord : {};
8161
- const resolvedModernizationPacketId = cleanText(packetRecordPayload.modernization_packet_id)
8162
- || cleanText(packetRecordPayload.modernizationPacketId)
8163
- || cleanText(packetRecordPayload.work_packet_id)
8164
- || cleanText(packetRecordPayload.workPacketId)
8165
- || normalizedModernizationPacketId
8166
- || null;
8167
- let wrapperExecutionRequestRecord = null;
8168
- const wrapperRequestLookupSurface = pickSurface([
8169
- 'getModernizationWrapperExecutionRequestRecord',
8170
- 'getWrapperExecutionRequestRecord',
8171
- 'getModernizationWrapperExecutionRequest',
8172
- 'getWrapperExecutionRequest',
8173
- ]);
8174
- if (wrapperRequestLookupSurface) {
8175
- wrapperExecutionRequestRecord = await wrapperRequestLookupSurface({
8176
- projectId: resolvedProjectId,
8177
- workflowId: resolvedWorkflowId,
8178
- workflowRunId: resolvedWorkflowRunId,
8179
- modernizationPacketId: resolvedModernizationPacketId,
8180
- wrapperExecutionRequestId: normalizedWrapperExecutionRequestId,
8181
- wrapperExecutionId: normalizedWrapperExecutionId,
8182
- sourceRef: normalizedSourceRef,
8183
- metadata: {
8184
- ...normalizedMetadata,
8185
- source: 'getModernizationWrapperEvidence',
8186
- },
8187
- }).catch(() => null);
8188
- }
8189
-
8190
- const requestRecordPayload = isPlainObject(wrapperExecutionRequestRecord) ? wrapperExecutionRequestRecord : {};
8191
- const resolvedWrapperExecutionRequestId = cleanText(requestRecordPayload.wrapper_execution_request_id)
8192
- || cleanText(requestRecordPayload.wrapperExecutionRequestId)
8193
- || cleanText(requestRecordPayload.execution_request_id)
8194
- || cleanText(requestRecordPayload.executionRequestId)
8195
- || normalizedWrapperExecutionRequestId
8196
- || null;
8197
-
8198
- let wrapperExecutionEvidenceBundle = null;
8199
- const wrapperEvidenceLookupSurface = pickSurface([
8200
- 'getWrapperExecutionEvidenceBundle',
8201
- 'getLatestWrapperExecutionEvidenceBundle',
8202
- 'getModernizationWrapperEvidenceBundle',
8203
- 'getModernizationWrapperExecutionEvidenceBundle',
8204
- ]);
8205
- if (wrapperEvidenceLookupSurface) {
8206
- wrapperExecutionEvidenceBundle = await wrapperEvidenceLookupSurface({
8207
- projectId: resolvedProjectId,
8208
- workflowId: resolvedWorkflowId,
8209
- workflowRunId: resolvedWorkflowRunId,
8210
- modernizationPacketId: resolvedModernizationPacketId,
8211
- wrapperExecutionRequestId: resolvedWrapperExecutionRequestId,
8212
- wrapperExecutionId: normalizedWrapperExecutionId,
8213
- includeOperations: Boolean(normalizedIncludeOperations),
8214
- includeFileManifest: Boolean(normalizedIncludeFileManifest),
8215
- includeVerificationSummary: Boolean(normalizedIncludeVerificationSummary),
8216
- includeProjection: Boolean(normalizedIncludeProjection),
8217
- metadata: {
8218
- ...normalizedMetadata,
8219
- source: 'getModernizationWrapperEvidence',
8220
- },
8221
- }).catch(() => null);
8222
- }
8223
-
8224
- const bundlePayload = isPlainObject(wrapperExecutionEvidenceBundle) ? wrapperExecutionEvidenceBundle : {};
8225
- let wrapperExecutionRecord = isPlainObject(bundlePayload.wrapper_execution_record)
8226
- ? bundlePayload.wrapper_execution_record
8227
- : isPlainObject(bundlePayload.wrapperExecutionRecord)
8228
- ? bundlePayload.wrapperExecutionRecord
8229
- : null;
8230
- let wrapperOperationRecords = Array.isArray(bundlePayload.wrapper_operation_records)
8231
- ? bundlePayload.wrapper_operation_records.filter((record) => isPlainObject(record))
8232
- : Array.isArray(bundlePayload.wrapperOperationRecords)
8233
- ? bundlePayload.wrapperOperationRecords.filter((record) => isPlainObject(record))
8234
- : [];
8235
- let wrapperFileManifest = isPlainObject(bundlePayload.wrapper_file_manifest)
8236
- ? bundlePayload.wrapper_file_manifest
8237
- : isPlainObject(bundlePayload.wrapperFileManifest)
8238
- ? bundlePayload.wrapperFileManifest
8239
- : null;
8240
- let wrapperVerificationSummary = isPlainObject(bundlePayload.wrapper_verification_summary)
8241
- ? bundlePayload.wrapper_verification_summary
8242
- : isPlainObject(bundlePayload.wrapperVerificationSummary)
8243
- ? bundlePayload.wrapperVerificationSummary
8244
- : null;
8245
-
8246
- if (!wrapperExecutionEvidenceBundle) {
8247
- if (!wrapperExecutionRecord) {
8248
- const wrapperRecordSurface = pickSurface([
8249
- 'getWrapperExecutionRecord',
8250
- 'getModernizationWrapperExecutionRecord',
8251
- ]);
8252
- if (wrapperRecordSurface) {
8253
- const wrapperRecordResult = await wrapperRecordSurface({
8254
- projectId: resolvedProjectId,
8255
- workflowId: resolvedWorkflowId,
8256
- workflowRunId: resolvedWorkflowRunId,
8257
- modernizationPacketId: resolvedModernizationPacketId,
8258
- wrapperExecutionRequestId: resolvedWrapperExecutionRequestId,
8259
- wrapperExecutionId: normalizedWrapperExecutionId,
8260
- sourceRef: normalizedSourceRef,
8261
- metadata: {
8262
- ...normalizedMetadata,
8263
- source: 'getModernizationWrapperEvidence',
8264
- },
8265
- }).catch(() => null);
8266
- if (isPlainObject(wrapperRecordResult)) {
8267
- wrapperExecutionRecord = wrapperRecordResult;
8268
- }
8269
- }
8270
- }
8271
-
8272
- if (normalizedIncludeOperations && wrapperOperationRecords.length === 0) {
8273
- const wrapperOperationSurface = pickSurface([
8274
- 'getWrapperExecutionOperationLog',
8275
- 'getModernizationWrapperExecutionOperationLog',
8276
- 'listWrapperExecutionOperations',
8277
- ]);
8278
- if (wrapperOperationSurface) {
8279
- const wrapperOperationResult = await wrapperOperationSurface({
8280
- projectId: resolvedProjectId,
8281
- workflowId: resolvedWorkflowId,
8282
- workflowRunId: resolvedWorkflowRunId,
8283
- modernizationPacketId: resolvedModernizationPacketId,
8284
- wrapperExecutionRequestId: resolvedWrapperExecutionRequestId,
8285
- wrapperExecutionId: normalizedWrapperExecutionId,
8286
- includeProjection: Boolean(normalizedIncludeProjection),
8287
- metadata: {
8288
- ...normalizedMetadata,
8289
- source: 'getModernizationWrapperEvidence',
8290
- },
8291
- }).catch(() => null);
8292
- if (Array.isArray(wrapperOperationResult)) {
8293
- wrapperOperationRecords = wrapperOperationResult.filter((record) => isPlainObject(record));
8294
- } else if (isPlainObject(wrapperOperationResult)) {
8295
- const maybeRecords = Array.isArray(wrapperOperationResult.wrapper_operation_records)
8296
- ? wrapperOperationResult.wrapper_operation_records
8297
- : Array.isArray(wrapperOperationResult.operation_records)
8298
- ? wrapperOperationResult.operation_records
8299
- : [];
8300
- wrapperOperationRecords = maybeRecords.filter((record) => isPlainObject(record));
8301
- }
8302
- }
8303
- }
8304
-
8305
- if (normalizedIncludeFileManifest && !wrapperFileManifest) {
8306
- const wrapperFileManifestSurface = pickSurface([
8307
- 'getWrapperExecutionFileManifest',
8308
- 'getModernizationWrapperExecutionFileManifest',
8309
- ]);
8310
- if (wrapperFileManifestSurface) {
8311
- const wrapperFileManifestResult = await wrapperFileManifestSurface({
8312
- projectId: resolvedProjectId,
8313
- workflowId: resolvedWorkflowId,
8314
- workflowRunId: resolvedWorkflowRunId,
8315
- modernizationPacketId: resolvedModernizationPacketId,
8316
- wrapperExecutionRequestId: resolvedWrapperExecutionRequestId,
8317
- wrapperExecutionId: normalizedWrapperExecutionId,
8318
- metadata: {
8319
- ...normalizedMetadata,
8320
- source: 'getModernizationWrapperEvidence',
8321
- },
8322
- }).catch(() => null);
8323
- if (isPlainObject(wrapperFileManifestResult)) {
8324
- wrapperFileManifest = wrapperFileManifestResult;
8325
- }
8326
- }
8327
- }
8328
-
8329
- if (normalizedIncludeVerificationSummary && !wrapperVerificationSummary) {
8330
- const wrapperVerificationSurface = pickSurface([
8331
- 'getWrapperExecutionVerificationSummary',
8332
- 'getModernizationWrapperExecutionVerificationSummary',
8333
- ]);
8334
- if (wrapperVerificationSurface) {
8335
- const wrapperVerificationResult = await wrapperVerificationSurface({
8336
- projectId: resolvedProjectId,
8337
- workflowId: resolvedWorkflowId,
8338
- workflowRunId: resolvedWorkflowRunId,
8339
- modernizationPacketId: resolvedModernizationPacketId,
8340
- wrapperExecutionRequestId: resolvedWrapperExecutionRequestId,
8341
- wrapperExecutionId: normalizedWrapperExecutionId,
8342
- metadata: {
8343
- ...normalizedMetadata,
8344
- source: 'getModernizationWrapperEvidence',
8345
- },
8346
- }).catch(() => null);
8347
- if (isPlainObject(wrapperVerificationResult)) {
8348
- wrapperVerificationSummary = wrapperVerificationResult;
8349
- }
8350
- }
8351
- }
8352
- }
8353
-
8354
- const wrapperExecutionIdResolved = cleanText(wrapperExecutionRecord?.execution_id)
8355
- || cleanText(wrapperExecutionRecord?.executionId)
8356
- || cleanText(bundlePayload.wrapper_execution_persistence?.wrapper_execution_record_id)
8357
- || cleanText(bundlePayload.wrapper_execution_persistence?.wrapperExecutionRecordId)
8358
- || normalizedWrapperExecutionId
8359
- || null;
8360
- const executionStatus = cleanText(wrapperExecutionRecord?.status)
8361
- || cleanText(bundlePayload.wrapper_execution_record?.status)
8362
- || cleanText(bundlePayload.wrapperExecutionRecord?.status)
8363
- || null;
8364
- const verificationStatus = cleanText(wrapperVerificationSummary?.verification_status)
8365
- || cleanText(wrapperVerificationSummary?.verificationStatus)
8366
- || cleanText(bundlePayload.wrapper_verification_summary?.verification_status)
8367
- || cleanText(bundlePayload.wrapperVerificationSummary?.verificationStatus)
8368
- || null;
8369
-
8370
- const filesCreated = Array.isArray(wrapperFileManifest?.files_created)
8371
- ? wrapperFileManifest.files_created
8372
- : Array.isArray(wrapperFileManifest?.filesCreated)
8373
- ? wrapperFileManifest.filesCreated
8374
- : [];
8375
- const filesModified = Array.isArray(wrapperFileManifest?.files_modified)
8376
- ? wrapperFileManifest.files_modified
8377
- : Array.isArray(wrapperFileManifest?.filesModified)
8378
- ? wrapperFileManifest.filesModified
8379
- : [];
8380
- const filesCreatedOrModified = cleanList([...filesCreated, ...filesModified]);
8381
-
8382
- const collectedEvidenceRefs = [];
8383
- const collectEvidenceRefs = (value) => {
8384
- if (Array.isArray(value)) {
8385
- for (const item of value) {
8386
- if (item !== null && item !== undefined && item !== '') {
8387
- collectedEvidenceRefs.push(item);
8388
- }
8389
- }
8390
- return;
8391
- }
8392
- if (value !== null && value !== undefined && value !== '') {
8393
- collectedEvidenceRefs.push(value);
8394
- }
8395
- };
8396
- collectEvidenceRefs(bundlePayload.evidence_refs);
8397
- collectEvidenceRefs(bundlePayload.evidenceRefs);
8398
- collectEvidenceRefs(wrapperExecutionRecord?.evidence_refs);
8399
- collectEvidenceRefs(wrapperExecutionRecord?.evidenceRefs);
8400
- collectEvidenceRefs(wrapperVerificationSummary?.evidence_refs);
8401
- collectEvidenceRefs(wrapperVerificationSummary?.evidenceRefs);
8402
- collectEvidenceRefs(requestRecordPayload.evidence_refs);
8403
- collectEvidenceRefs(requestRecordPayload.evidenceRefs);
8404
- collectEvidenceRefs(packetRecordPayload.evidence_refs);
8405
- collectEvidenceRefs(packetRecordPayload.evidenceRefs);
8406
- if (Array.isArray(wrapperOperationRecords)) {
8407
- for (const record of wrapperOperationRecords) {
8408
- collectEvidenceRefs(record?.evidence_ref);
8409
- collectEvidenceRefs(record?.evidenceRefs);
8410
- collectEvidenceRefs(record?.evidence_refs);
8411
- }
8412
- }
8413
- const uniqueEvidenceRefs = [];
8414
- const seenEvidenceRefKeys = new Set();
8415
- for (const evidenceRef of collectedEvidenceRefs) {
8416
- const key = typeof evidenceRef === 'string'
8417
- ? evidenceRef.trim()
8418
- : JSON.stringify(evidenceRef);
8419
- if (!key || seenEvidenceRefKeys.has(key)) continue;
8420
- seenEvidenceRefKeys.add(key);
8421
- uniqueEvidenceRefs.push(evidenceRef);
8422
- }
8423
-
8424
- const transferPacketId = cleanText(wrapperExecutionRequestRecord?.transfer_packet_id)
8425
- || cleanText(wrapperExecutionRequestRecord?.work_transfer_packet_id)
8426
- || cleanText(wrapperExecutionRequestRecord?.workTransferPacketId)
8427
- || cleanText(bundlePayload.transfer_packet_id)
8428
- || cleanText(bundlePayload.transferPacketId)
8429
- || null;
8430
- const transferChannelId = cleanText(wrapperExecutionRequestRecord?.transfer_channel_id)
8431
- || cleanText(wrapperExecutionRequestRecord?.channel_id)
8432
- || cleanText(wrapperExecutionRequestRecord?.channelId)
8433
- || cleanText(bundlePayload.transfer_channel_id)
8434
- || cleanText(bundlePayload.transferChannelId)
8435
- || null;
8436
-
8437
- let transferPacketProjection = null;
8438
- if (normalizedIncludeProjection && transferPacketId) {
8439
- if (typeof this.getTransferPacketProjection === 'function') {
8440
- transferPacketProjection = await this.getTransferPacketProjection(transferPacketId).catch(() => null);
8441
- } else {
8442
- missingSurfaces.push('getTransferPacketProjection');
8443
- }
8444
- }
8445
- let transferChannelProjection = null;
8446
- if (normalizedIncludeProjection && transferChannelId) {
8447
- const transferChannelProjectionSurface = typeof this.getTransferChannelProjection === 'function'
8448
- ? this.getTransferChannelProjection.bind(this)
8449
- : typeof this.getLogaTransferChannelThreadProjection === 'function'
8450
- ? this.getLogaTransferChannelThreadProjection.bind(this)
8451
- : null;
8452
- if (!transferChannelProjectionSurface) {
8453
- missingSurfaces.push('getTransferChannelProjection');
8454
- } else {
8455
- transferChannelProjection = await transferChannelProjectionSurface(transferChannelId).catch(() => null);
8456
- }
8457
- }
8458
-
8459
- const operationCount = wrapperOperationRecords.length;
8460
- const gateReady = Boolean(
8461
- wrapperExecutionRecord
8462
- && executionStatus === 'completed'
8463
- && verificationStatus === 'pass'
8464
- && operationCount > 0
8465
- && filesCreatedOrModified.length > 0
8466
- );
8467
-
8468
- return {
8469
- status: missingSurfaces.length > 0 ? 'partial' : 'ready',
8470
- project_id: resolvedProjectId || null,
8471
- modernization_packet_id: resolvedModernizationPacketId,
8472
- wrapper_execution_request_id: resolvedWrapperExecutionRequestId || null,
8473
- wrapper_execution_id: wrapperExecutionIdResolved,
8474
- execution_status: executionStatus,
8475
- operation_count: operationCount,
8476
- files_created_or_modified: filesCreatedOrModified,
8477
- verification_status: verificationStatus,
8478
- gate_ready: gateReady,
8479
- evidence_refs: uniqueEvidenceRefs,
8480
- operator_projection_metadata: {
8481
- portfolio_bundle: portfolioBundle,
8482
- project_roadmap_summary: projectRoadmapSummary,
8483
- project_roadmap_active_item: projectActiveItem,
8484
- modernization_packet_record: modernizationPacketRecord,
8485
- wrapper_execution_request_record: wrapperExecutionRequestRecord,
8486
- wrapper_execution_record: wrapperExecutionRecord,
8487
- wrapper_execution_evidence_bundle: wrapperExecutionEvidenceBundle,
8488
- wrapper_operation_records: wrapperOperationRecords,
8489
- wrapper_file_manifest: wrapperFileManifest,
8490
- wrapper_verification_summary: wrapperVerificationSummary,
8491
- transfer_packet_projection: transferPacketProjection,
8492
- transfer_channel_projection: transferChannelProjection,
8493
- },
8494
- missing_surfaces: [...new Set(missingSurfaces)],
8495
- project: projectPayload,
8496
- modernization_packet_record: modernizationPacketRecord,
8497
- wrapper_execution_request_record: wrapperExecutionRequestRecord,
8498
- wrapper_execution_record: wrapperExecutionRecord,
8499
- wrapper_execution_evidence_bundle: wrapperExecutionEvidenceBundle,
8500
- wrapper_operation_records: wrapperOperationRecords,
8501
- wrapper_file_manifest: wrapperFileManifest,
8502
- wrapper_verification_summary: wrapperVerificationSummary,
8503
- transfer_packet_projection: transferPacketProjection,
8504
- transfer_channel_projection: transferChannelProjection,
8505
- };
8506
- }
8507
-
8508
- async decideModernizationGate({
8509
- projectIdentifier,
8510
- projectId,
8511
- relatedProjectId,
8512
- related_project_id,
8513
- startWorkBody = {},
8514
- startWorkRequest = {},
8515
- resumeProjectWorkOptions = {},
8516
- modernizationPacket = {},
8517
- modernization_packet = {},
8518
- modernizationPacketId,
8519
- modernization_packet_id,
8520
- packetId,
8521
- packet_id,
8522
- wrapperExecutionRequestId,
8523
- wrapper_execution_request_id,
8524
- wrapperExecutionId,
8525
- wrapper_execution_id,
8526
- sourceRef,
8527
- source_ref,
8528
- requiredEvidence = [],
8529
- required_evidence = [],
8530
- acceptanceCriteria = [],
8531
- acceptance_criteria = [],
8532
- decisionMode,
8533
- decision_mode,
8534
- decisionBand,
8535
- decision_band,
8536
- reviewer,
8537
- reviewer_id,
8538
- assignedReviewer,
8539
- assigned_reviewer,
8540
- reviewRequired,
8541
- review_required,
8542
- riskLevel,
8543
- risk_level,
8544
- rationale,
8545
- rationaleText,
8546
- decisionRationale,
8547
- decision_rationale,
8548
- recordDecision,
8549
- record_decision,
8550
- reviewChannelId,
8551
- review_channel_id,
8552
- channelId,
8553
- channel_id,
8554
- transferChannelId,
8555
- transfer_channel_id,
8556
- implementationItemId,
8557
- implementation_item_id,
8558
- submittedBy,
8559
- submitted_by,
8560
- includeProjection = true,
8561
- include_projection = true,
8562
- metadata = {},
8563
- } = {}) {
8564
- const missingSurfaces = [];
8565
- const normalizedMetadata = isPlainObject(metadata) ? metadata : {};
8566
- const normalizedPacket = isPlainObject(modernization_packet)
8567
- ? modernization_packet
8568
- : (isPlainObject(modernizationPacket) ? modernizationPacket : {});
8569
- const normalizedProjectReference = cleanText(projectIdentifier)
8570
- || cleanText(projectId)
8571
- || cleanText(relatedProjectId)
8572
- || cleanText(related_project_id)
8573
- || cleanText(normalizedPacket.project_id)
8574
- || cleanText(normalizedPacket.projectId)
8575
- || cleanText(normalizedPacket.related_project_id)
8576
- || cleanText(normalizedPacket.relatedProjectId);
8577
- const normalizedModernizationPacketId = cleanText(modernization_packet_id)
8578
- || cleanText(modernizationPacketId)
8579
- || cleanText(packet_id)
8580
- || cleanText(packetId)
8581
- || cleanText(normalizedPacket.modernization_packet_id)
8582
- || cleanText(normalizedPacket.modernizationPacketId)
8583
- || cleanText(normalizedPacket.packet_id)
8584
- || cleanText(normalizedPacket.packetId);
8585
- const normalizedWrapperExecutionRequestId = cleanText(wrapper_execution_request_id)
8586
- || cleanText(wrapperExecutionRequestId);
8587
- const normalizedWrapperExecutionId = cleanText(wrapper_execution_id)
8588
- || cleanText(wrapperExecutionId);
8589
- const normalizedSourceRef = cleanText(source_ref)
8590
- || cleanText(sourceRef)
8591
- || cleanText(normalizedPacket.source_ref)
8592
- || cleanText(normalizedPacket.sourceRef)
8593
- || normalizedModernizationPacketId
8594
- || normalizedWrapperExecutionRequestId
8595
- || normalizedWrapperExecutionId;
8596
- const normalizedRequiredEvidence = cleanList(required_evidence.length > 0 ? required_evidence : requiredEvidence);
8597
- const normalizedAcceptanceCriteria = cleanList(acceptance_criteria.length > 0 ? acceptance_criteria : acceptanceCriteria);
8598
- const normalizedDecisionMode = cleanText(decision_mode) || cleanText(decisionMode) || 'recommend_only';
8599
- const normalizedDecisionBand = cleanText(decision_band) || cleanText(decisionBand) || null;
8600
- const normalizedReviewer = cleanText(reviewer_id)
8601
- || cleanText(reviewer)
8602
- || cleanText(assigned_reviewer)
8603
- || cleanText(assignedReviewer)
8604
- || null;
8605
- const normalizedReviewRequired = review_required ?? reviewRequired ?? Boolean(normalizedReviewer);
8606
- const normalizedRiskLevel = cleanText(risk_level) || cleanText(riskLevel) || cleanText(normalizedPacket.risk_level) || cleanText(normalizedPacket.riskLevel) || null;
8607
- const normalizedRationale = cleanText(decision_rationale)
8608
- || cleanText(decisionRationale)
8609
- || cleanText(rationaleText)
8610
- || cleanText(rationale)
8611
- || null;
8612
- const normalizedRecordDecision = record_decision ?? recordDecision ?? normalizedDecisionMode === 'record_decision';
8613
- const normalizedSubmittedBy = cleanText(submitted_by)
8614
- || cleanText(submittedBy)
8615
- || this.agentSessionId
8616
- || this.actorId
8617
- || null;
8618
- const normalizedReviewChannelId = cleanText(review_channel_id)
8619
- || cleanText(reviewChannelId)
8620
- || cleanText(channel_id)
8621
- || cleanText(channelId)
8622
- || cleanText(transfer_channel_id)
8623
- || cleanText(transferChannelId)
8624
- || null;
8625
-
8626
- const pickSurface = (candidateNames) => {
8627
- for (const candidateName of candidateNames) {
8628
- if (typeof this[candidateName] === 'function') {
8629
- return this[candidateName].bind(this);
8630
- }
8631
- }
8632
- for (const candidateName of candidateNames) {
8633
- missingSurfaces.push(candidateName);
8634
- }
8635
- return null;
8636
- };
8637
-
8638
- let continuation = null;
8639
- let startWorkResult = null;
8640
- if (normalizedProjectReference) {
8641
- if (typeof this.resumeProjectWork === 'function') {
8642
- continuation = await this.resumeProjectWork({
8643
- projectIdentifier: normalizedProjectReference,
8644
- projectId: normalizedProjectReference,
8645
- requireClaim: false,
8646
- ...resumeProjectWorkOptions,
8647
- });
8648
- } else {
8649
- missingSurfaces.push('resumeProjectWork');
8650
- }
8651
- } else if (Object.keys(isPlainObject(startWorkRequest) ? startWorkRequest : {}).length > 0 || Object.keys(isPlainObject(startWorkBody) ? startWorkBody : {}).length > 0) {
8652
- if (typeof this.startWork === 'function') {
8653
- startWorkResult = await this.startWork({
8654
- ...(isPlainObject(startWorkBody) ? startWorkBody : {}),
8655
- ...(isPlainObject(startWorkRequest) ? startWorkRequest : {}),
8656
- });
8657
- } else {
8658
- missingSurfaces.push('startWork');
8659
- }
8660
- }
8661
-
8662
- const projectPayload = isPlainObject(continuation?.project)
8663
- ? continuation.project
8664
- : isPlainObject(startWorkResult?.project)
8665
- ? startWorkResult.project
8666
- : isPlainObject(continuation?.summary)
8667
- ? continuation.summary
8668
- : {};
8669
- const resolvedProjectId = cleanText(projectPayload.project_id)
8670
- || cleanText(projectPayload.projectId)
8671
- || normalizedProjectReference;
8672
- const resolvedWorkflowId = cleanText(projectPayload.workflow_id)
8673
- || cleanText(projectPayload.workflowId)
8674
- || cleanText(continuation?.workflow_id)
8675
- || cleanText(startWorkResult?.workflow_id);
8676
- const resolvedWorkflowRunId = cleanText(projectPayload.workflow_run_id)
8677
- || cleanText(projectPayload.workflowRunId)
8678
- || cleanText(continuation?.workflow_run_id)
8679
- || cleanText(startWorkResult?.workflow_run_id)
8680
- || cleanText(startWorkResult?.workflowRunId);
8681
-
8682
- const wrapperEvidence = await this.getModernizationWrapperEvidence({
8683
- projectIdentifier: resolvedProjectId || normalizedProjectReference,
8684
- modernizationPacketId: normalizedModernizationPacketId,
8685
- wrapperExecutionRequestId: normalizedWrapperExecutionRequestId,
8686
- wrapperExecutionId: normalizedWrapperExecutionId,
8687
- sourceRef: normalizedSourceRef,
8688
- includeOperations: true,
8689
- includeFileManifest: true,
8690
- includeVerificationSummary: true,
8691
- includeProjection: Boolean(include_projection ?? includeProjection ?? true),
8692
- metadata: {
8693
- ...normalizedMetadata,
8694
- source: 'decideModernizationGate',
8695
- },
8696
- }).catch(() => null);
8697
- const wrapperEvidencePayload = isPlainObject(wrapperEvidence) ? wrapperEvidence : {};
8698
-
8699
- let modernizationPacketRecord = isPlainObject(wrapperEvidencePayload.modernization_packet_record)
8700
- ? wrapperEvidencePayload.modernization_packet_record
8701
- : null;
8702
- const packetLookupSurface = typeof this.getModernizationWorkPacketRecord === 'function'
8703
- ? this.getModernizationWorkPacketRecord.bind(this)
8704
- : typeof this.getModernizationPacketRecord === 'function'
8705
- ? this.getModernizationPacketRecord.bind(this)
8706
- : null;
8707
- if (!modernizationPacketRecord && packetLookupSurface) {
8708
- modernizationPacketRecord = await packetLookupSurface({
8709
- projectId: resolvedProjectId,
8710
- workflowId: resolvedWorkflowId,
8711
- workflowRunId: resolvedWorkflowRunId,
8712
- modernizationPacketId: normalizedModernizationPacketId,
8713
- packetId: normalizedModernizationPacketId,
8714
- sourceRef: normalizedSourceRef,
8715
- metadata: {
8716
- ...normalizedMetadata,
8717
- source: 'decideModernizationGate',
8718
- },
8719
- }).catch(() => null);
8720
- }
8721
- if (!packetLookupSurface) {
8722
- missingSurfaces.push('getModernizationWorkPacketRecord');
8723
- missingSurfaces.push('getModernizationPacketRecord');
8724
- }
8725
-
8726
- const packetRecordPayload = isPlainObject(modernizationPacketRecord) ? modernizationPacketRecord : {};
8727
- const resolvedModernizationPacketId = cleanText(packetRecordPayload.modernization_packet_id)
8728
- || cleanText(packetRecordPayload.modernizationPacketId)
8729
- || cleanText(packetRecordPayload.work_packet_id)
8730
- || cleanText(packetRecordPayload.workPacketId)
8731
- || normalizedModernizationPacketId
8732
- || null;
8733
- const resolvedImplementationItemId = cleanText(packetRecordPayload.implementation_item_id)
8734
- || cleanText(packetRecordPayload.implementationItemId)
8735
- || cleanText(packetRecordPayload.roadmap_item_id)
8736
- || cleanText(packetRecordPayload.roadmapItemId)
8737
- || cleanText(implementation_item_id)
8738
- || cleanText(implementationItemId)
8739
- || null;
8740
-
8741
- const acceptanceCriteriaResolved = normalizedAcceptanceCriteria.length > 0
8742
- ? normalizedAcceptanceCriteria
8743
- : cleanList(packetRecordPayload.acceptance_criteria || packetRecordPayload.acceptanceCriteria || []);
8744
- const evidenceRefs = cleanList([
8745
- ...(Array.isArray(wrapperEvidencePayload.evidence_refs) ? wrapperEvidencePayload.evidence_refs : []),
8746
- ...(Array.isArray(wrapperEvidencePayload.evidenceRefs) ? wrapperEvidencePayload.evidenceRefs : []),
8747
- ]);
8748
- const requiredEvidenceMissing = normalizedRequiredEvidence.filter((evidenceRef) => !evidenceRefs.includes(evidenceRef));
8749
- const evidenceStatus = wrapperEvidencePayload.wrapper_execution_record
8750
- ? (wrapperEvidencePayload.gate_ready === true ? 'complete' : 'incomplete')
8751
- : 'absent';
8752
- const verificationStatus = cleanText(wrapperEvidencePayload.verification_status)
8753
- || cleanText(wrapperEvidencePayload.wrapper_verification_summary?.verification_status)
8754
- || cleanText(wrapperEvidencePayload.wrapper_verification_summary?.verificationStatus)
8755
- || null;
8756
- const evidenceComplete = evidenceStatus === 'complete' && verificationStatus === 'pass' && requiredEvidenceMissing.length === 0;
8757
- const reviewChannelPresent = Boolean(normalizedReviewChannelId);
8758
- const reviewRequiredResolved = Boolean(normalizedReviewRequired || evidenceStatus !== 'complete' || verificationStatus !== 'pass' || requiredEvidenceMissing.length > 0 || acceptanceCriteriaResolved.length === 0);
8759
- const recommendedDecision = evidenceComplete ? 'pass' : (reviewRequiredResolved ? 'pending' : 'block');
8760
- const gateDecisionRequested = normalizedRecordDecision && evidenceComplete;
8761
-
8762
- let gateDecisionResult = null;
8763
- const gateDecisionSurface = typeof this.createImplementationPacketGateDecision === 'function'
8764
- ? this.createImplementationPacketGateDecision.bind(this)
8765
- : null;
8766
- if (normalizedRecordDecision && !gateDecisionSurface) {
8767
- missingSurfaces.push('createImplementationPacketGateDecision');
8768
- }
8769
- if (normalizedRecordDecision && gateDecisionSurface) {
8770
- gateDecisionResult = await gateDecisionSurface(resolvedModernizationPacketId, {
8771
- gate_type: cleanText(normalizedDecisionBand) || 'modernization_gate',
8772
- decision: evidenceComplete ? 'pass' : 'block',
8773
- rationale: normalizedRationale
8774
- || (evidenceComplete
8775
- ? 'Wrapper evidence satisfies the modernization gate.'
8776
- : 'Wrapper evidence is incomplete or unavailable; the gate cannot be marked as pass.'),
8777
- evidence_refs: evidenceRefs,
8778
- remediation_actions: evidenceComplete ? [] : ['Review wrapper evidence and missing modernization surfaces.'],
8779
- decided_by: normalizedReviewer || normalizedSubmittedBy || this.agentSessionId || this.actorId || null,
8780
- }).catch(() => null);
8781
- }
8782
-
8783
- let gateDecisionId = cleanText(gateDecisionResult?.implementation_gate_decision_id)
8784
- || cleanText(gateDecisionResult?.gate_decision_id)
8785
- || null;
8786
-
8787
- let evidenceLinkResult = null;
8788
- if (gateDecisionId && typeof this.addImplementationItemEvidence === 'function' && resolvedImplementationItemId) {
8789
- evidenceLinkResult = await this.addImplementationItemEvidence(
8790
- resolvedImplementationItemId,
8791
- {
8792
- evidence_type: 'modernization_gate_decision',
8793
- evidence_ref: gateDecisionId,
8794
- title: `Modernization gate decision for ${resolvedModernizationPacketId}`,
8795
- recorded_by: normalizedReviewer || normalizedSubmittedBy || this.agentSessionId || this.actorId || null,
8796
- metadata: {
8797
- ...normalizedMetadata,
8798
- source: 'decideModernizationGate',
8799
- modernization_packet_id: resolvedModernizationPacketId,
8800
- wrapper_execution_id: cleanText(wrapperEvidencePayload.wrapper_execution_id) || cleanText(wrapperEvidencePayload.wrapper_execution_record?.execution_id) || null,
8801
- decision: gateDecisionResult?.decision || (evidenceComplete ? 'pass' : 'block'),
8802
- },
8803
- },
8804
- ).catch(() => null);
8805
- } else if (gateDecisionId && !resolvedImplementationItemId) {
8806
- missingSurfaces.push('addImplementationItemEvidence');
8807
- }
8808
-
8809
- let watchResult = null;
8810
- if (reviewRequiredResolved && reviewChannelPresent) {
8811
- const watchSurface = typeof this.startMessageWatch === 'function'
8812
- ? this.startMessageWatch.bind(this)
8813
- : null;
8814
- if (!watchSurface) {
8815
- missingSurfaces.push('startMessageWatch');
8816
- } else {
8817
- watchResult = await watchSurface({
8818
- transferChannelId: normalizedReviewChannelId,
8819
- workTransferPacketId: cleanText(wrapperEvidencePayload.transfer_packet_id) || cleanText(wrapperEvidencePayload.modernization_packet_id) || resolvedModernizationPacketId,
8820
- workflowRunId: resolvedWorkflowRunId,
8821
- watchingAgentRole: normalizedSubmittedBy,
8822
- expectedFromRole: normalizedReviewer || 'reviewer',
8823
- expectedMessageKind: 'response',
8824
- watchType: 'expected_peer_message',
8825
- watchingAgentSessionId: normalizedSubmittedBy,
8826
- expectedPayload: {
8827
- modernization_packet_id: resolvedModernizationPacketId,
8828
- expected_decision: recommendedDecision,
8829
- },
8830
- currentStatus: 'watching',
8831
- operatorNudge: normalizedRationale || 'Review modernization gate recommendation.',
8832
- metadata: {
8833
- ...normalizedMetadata,
8834
- source: 'decideModernizationGate',
8835
- },
8836
- }).catch(() => null);
8837
- }
8838
- }
8839
-
8840
- const watchPayload = isPlainObject(watchResult) ? watchResult : {};
8841
- const watchId = cleanText(watchPayload.message_watch_id)
8842
- || cleanText(watchPayload.watch_id)
8843
- || cleanText(watchPayload.message_watch?.message_watch_id)
8844
- || cleanText(watchPayload.message_watch?.watch_id)
8845
- || null;
8846
-
8847
- let heartbeatResult = null;
8848
- if (reviewChannelPresent && typeof this.postCollaborationHeartbeat === 'function') {
8849
- heartbeatResult = await this.postCollaborationHeartbeat({
8850
- transferChannelId: normalizedReviewChannelId,
8851
- workTransferPacketId: cleanText(wrapperEvidencePayload.transfer_packet_id) || resolvedModernizationPacketId,
8852
- workflowRunId: resolvedWorkflowRunId,
8853
- participantRole: normalizedReviewer ? 'reviewer' : 'gate_reviewer',
8854
- currentPhase: 'modernization_gate_review',
8855
- currentTaskSummary: normalizedRationale || `Review modernization gate for ${resolvedModernizationPacketId}`,
8856
- metadata: {
8857
- ...normalizedMetadata,
8858
- source: 'decideModernizationGate',
8859
- },
8860
- }).catch(() => null);
8861
- } else if (reviewChannelPresent) {
8862
- missingSurfaces.push('postCollaborationHeartbeat');
8863
- }
8864
- const heartbeatPayload = isPlainObject(heartbeatResult) ? heartbeatResult : {};
8865
- const heartbeatStatus = cleanText(heartbeatPayload.collaboration_heartbeat?.activity_state)
8866
- || cleanText(heartbeatPayload.activity_state)
8867
- || cleanText(heartbeatPayload.status)
8868
- || null;
8869
-
8870
- const projectRoadmapSummary = resolvedProjectId && typeof this.getProjectRoadmapSummary === 'function'
8871
- ? await this.getProjectRoadmapSummary(resolvedProjectId).catch(() => null)
8872
- : null;
8873
- const projectActiveItem = resolvedProjectId && typeof this.getProjectRoadmapActiveItem === 'function'
8874
- ? await this.getProjectRoadmapActiveItem(resolvedProjectId).catch(() => null)
8875
- : null;
8876
- let projectProjection = null;
8877
- if (include_projection ?? includeProjection ?? true) {
8878
- const projectProjectionSurface = typeof this.getLogaProjectPortfolioProjection === 'function'
8879
- ? this.getLogaProjectPortfolioProjection.bind(this)
8880
- : null;
8881
- if (projectProjectionSurface) {
8882
- projectProjection = await projectProjectionSurface().catch(() => null);
8883
- } else {
8884
- missingSurfaces.push('getLogaProjectPortfolioProjection');
8885
- }
8886
- }
8887
-
8888
- const operatorProjectionMetadata = {
8889
- modernization_packet_record: modernizationPacketRecord,
8890
- wrapper_evidence: wrapperEvidencePayload,
8891
- gate_decision: gateDecisionResult,
8892
- gate_decision_evidence_link: evidenceLinkResult,
8893
- project_roadmap_summary: projectRoadmapSummary,
8894
- project_roadmap_active_item: projectActiveItem,
8895
- project_projection: projectProjection,
8896
- review_watch: watchPayload,
8897
- review_heartbeat: heartbeatPayload,
8898
- acceptance_criteria: acceptanceCriteriaResolved,
8899
- };
8900
-
8901
- const status = evidenceComplete
8902
- ? (gateDecisionRequested ? 'ready' : (missingSurfaces.length > 0 ? 'partial' : 'ready'))
8903
- : (reviewRequiredResolved ? 'review_required' : 'blocked');
8904
-
8905
- return {
8906
- status,
8907
- project_id: resolvedProjectId || null,
8908
- modernization_packet_id: resolvedModernizationPacketId,
8909
- wrapper_execution_id: cleanText(wrapperEvidencePayload.wrapper_execution_id) || cleanText(wrapperEvidencePayload.wrapper_execution_record?.execution_id) || normalizedWrapperExecutionId || null,
8910
- evidence_status: evidenceStatus,
8911
- verification_status: verificationStatus,
8912
- recommended_decision: recommendedDecision,
8913
- gate_decision_id: gateDecisionId || null,
8914
- review_required: reviewRequiredResolved,
8915
- watch_id: watchId || null,
8916
- heartbeat_status: heartbeatStatus,
8917
- operator_projection_metadata: operatorProjectionMetadata,
8918
- missing_surfaces: [...new Set(missingSurfaces)],
8919
- project: projectPayload,
8920
- modernization_packet_record: modernizationPacketRecord,
8921
- wrapper_evidence: wrapperEvidencePayload,
8922
- gate_decision: gateDecisionResult,
8923
- gate_decision_evidence_link: evidenceLinkResult,
8924
- review_watch: watchPayload,
8925
- review_heartbeat: heartbeatPayload,
8926
- };
4680
+ async decideModernizationGate(request = {}) {
4681
+ return this.workflowComposition.decideModernizationGate(request);
8927
4682
  }
8928
-
8929
4683
  // ─── Self-Learning ─────────────────────────────────────────────────────────
8930
4684
 
8931
4685
  async getSelfLearningPosture({ workflowRunId, limit } = {}) {