@h9-foundry/agentforge-cli 0.10.0 → 0.11.0

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.
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { agentManifestSchema, agentOutputSchema, attestationVerificationEvidenceSchema, buildkiteCiEvidenceExportSchema, ciEvidenceSchema, deploymentGateArtifactSchema, deploymentGateEvidenceNormalizationSchema, deploymentRequestSchema, dependencyIntegrityEvidenceSchema, genericCiEvidenceExportSchema, designArtifactSchema, githubActionsEvidenceSchema, gitlabCiEvidenceExportSchema, jenkinsCiEvidenceExportSchema, implementationArtifactSchema, implementationInventorySchema, incidentArtifactSchema, incidentEvidenceNormalizationSchema, incidentRequestSchema, maintenanceArtifactSchema, maintenanceEvidenceNormalizationSchema, maintenanceRequestSchema, pipelineArtifactSchema, pipelineEvidenceNormalizationSchema, pipelineRequestSchema, qaArtifactSchema, qaEvidenceNormalizationSchema, qaRequestSchema, releaseApprovalRecommendationSchema, releaseArtifactSchema, releaseCiEvidenceSummarySchema, releaseEvidenceNormalizationSchema, releaseRequestSchema, securityArtifactSchema, securityEvidenceNormalizationSchema, securityRequestSchema, planningArtifactSchema } from "@h9-foundry/agentforge-schemas";
3
+ import { agentManifestSchema, agentOutputSchema, attestationVerificationEvidenceSchema, buildkiteCiEvidenceExportSchema, ciEvidenceSchema, deploymentGateArtifactSchema, deploymentGateEvidenceNormalizationSchema, deploymentRequestSchema, dependencyIntegrityEvidenceSchema, genericCiEvidenceExportSchema, designArtifactSchema, githubActionsEvidenceSchema, gitlabCiEvidenceExportSchema, jenkinsCiEvidenceExportSchema, implementationArtifactSchema, implementationInventorySchema, incidentArtifactSchema, incidentEvidenceNormalizationSchema, incidentRequestSchema, maintenanceArtifactSchema, maintenanceEvidenceNormalizationSchema, maintenanceRequestSchema, pipelineArtifactSchema, pipelineEvidenceNormalizationSchema, pipelineRequestSchema, qaArtifactSchema, qaEvidenceNormalizationSchema, qaRequestSchema, promotionApprovalArtifactSchema, promotionApprovalEvidenceNormalizationSchema, promotionRequestSchema, releaseApprovalRecommendationSchema, releaseArtifactSchema, releaseCiEvidenceSummarySchema, releaseEvidenceNormalizationSchema, releaseRequestSchema, securityArtifactSchema, securityEvidenceNormalizationSchema, securityRequestSchema, planningArtifactSchema } from "@h9-foundry/agentforge-schemas";
4
4
  const contextCollectorAgent = {
5
5
  manifest: agentManifestSchema.parse({
6
6
  version: 1,
@@ -421,6 +421,45 @@ function evaluatePipelineReportReadiness(bundlePath) {
421
421
  detail: `Using ${bundlePath} as a ready pipeline-report reference.`
422
422
  };
423
423
  }
424
+ function evaluateDeploymentGateApprovalReadiness(bundlePath, targetEnvironment) {
425
+ const artifact = loadBundleLifecycleArtifact(bundlePath, "deployment-gate-report");
426
+ if (!artifact) {
427
+ return {
428
+ ready: false,
429
+ detail: `Deployment gate bundle is missing a deployment-gate-report artifact: ${bundlePath}`
430
+ };
431
+ }
432
+ const parsed = deploymentGateArtifactSchema.safeParse(artifact);
433
+ if (!parsed.success) {
434
+ return {
435
+ ready: false,
436
+ detail: `Deployment gate bundle could not be parsed as a bounded deployment-gate-report artifact: ${bundlePath}`
437
+ };
438
+ }
439
+ const deploymentGateArtifact = parsed.data;
440
+ if (deploymentGateArtifact.status !== "complete") {
441
+ return {
442
+ ready: false,
443
+ detail: `Deployment gate report ${bundlePath} is ${deploymentGateArtifact.status} and cannot satisfy promotion approval yet.`
444
+ };
445
+ }
446
+ if (deploymentGateArtifact.payload.gateStatus !== "ready_for_approval") {
447
+ return {
448
+ ready: false,
449
+ detail: `Deployment gate report ${bundlePath} is ${deploymentGateArtifact.payload.gateStatus} and cannot satisfy promotion approval yet.`
450
+ };
451
+ }
452
+ if (targetEnvironment && deploymentGateArtifact.payload.targetEnvironment !== targetEnvironment) {
453
+ return {
454
+ ready: false,
455
+ detail: `Deployment gate report ${bundlePath} targets ${deploymentGateArtifact.payload.targetEnvironment}, not ${targetEnvironment}.`
456
+ };
457
+ }
458
+ return {
459
+ ready: true,
460
+ detail: `Using ${bundlePath} as a ready deployment-gate-report reference for ${targetEnvironment ?? deploymentGateArtifact.payload.targetEnvironment}.`
461
+ };
462
+ }
424
463
  function loadBundleArtifactPayloadPaths(bundlePath) {
425
464
  if (!existsSync(bundlePath)) {
426
465
  return [];
@@ -3184,6 +3223,372 @@ const deploymentGateAnalystAgent = {
3184
3223
  });
3185
3224
  }
3186
3225
  };
3226
+ const promotionApprovalIntakeAgent = {
3227
+ manifest: agentManifestSchema.parse({
3228
+ version: 1,
3229
+ name: "promotion-approval-intake",
3230
+ displayName: "Promotion Approval Intake",
3231
+ category: "release",
3232
+ runtime: {
3233
+ minVersion: "0.1.0",
3234
+ kind: "deterministic"
3235
+ },
3236
+ permissions: {
3237
+ model: false,
3238
+ network: false,
3239
+ tools: [],
3240
+ readPaths: [".agentops/requests/**", ".agentops/runs/**"],
3241
+ writePaths: []
3242
+ },
3243
+ inputs: ["workflowInputs", "repo"],
3244
+ outputs: ["summary", "metadata"],
3245
+ contextPolicy: {
3246
+ sections: ["workflowInputs", "repo", "context"],
3247
+ minimalContext: true
3248
+ },
3249
+ catalog: {
3250
+ domain: "release",
3251
+ supportLevel: "internal",
3252
+ maturity: "mvp",
3253
+ trustScope: "official-core-only"
3254
+ },
3255
+ trust: {
3256
+ tier: "core",
3257
+ source: "official",
3258
+ reviewed: true
3259
+ }
3260
+ }),
3261
+ outputSchema: agentOutputSchema,
3262
+ async execute({ stateSlice }) {
3263
+ const promotionRequest = getWorkflowInput(stateSlice, "promotionRequest");
3264
+ const promotionIssueRefs = getWorkflowInput(stateSlice, "promotionIssueRefs") ?? [];
3265
+ const promotionScmRefs = getWorkflowInput(stateSlice, "promotionScmRefs") ?? [];
3266
+ const promotionGithubRefs = getWorkflowInput(stateSlice, "promotionGithubRefs") ?? [];
3267
+ const requestFile = getWorkflowInput(stateSlice, "requestFile");
3268
+ if (!promotionRequest) {
3269
+ throw new Error("promotion-approval requires a validated promotion request before runtime execution.");
3270
+ }
3271
+ return agentOutputSchema.parse({
3272
+ summary: `Loaded promotion approval request from ${requestFile ?? ".agentops/requests/promotion.yaml"} for ${promotionRequest.targetEnvironment}.`,
3273
+ findings: [],
3274
+ proposedActions: [],
3275
+ lifecycleArtifacts: [],
3276
+ requestedTools: [],
3277
+ blockedActionFlags: [],
3278
+ metadata: {
3279
+ ...promotionRequestSchema.parse(promotionRequest),
3280
+ promotionIssueRefs,
3281
+ promotionScmRefs,
3282
+ promotionGithubRefs,
3283
+ evidenceSourceCount: promotionRequest.evidenceSources.length +
3284
+ promotionRequest.qaReportRefs.length +
3285
+ promotionRequest.securityReportRefs.length +
3286
+ promotionRequest.releaseReportRefs.length +
3287
+ promotionRequest.deploymentGateReportRefs.length
3288
+ }
3289
+ });
3290
+ }
3291
+ };
3292
+ const promotionApprovalEvidenceNormalizationAgent = {
3293
+ manifest: agentManifestSchema.parse({
3294
+ version: 1,
3295
+ name: "promotion-approval-evidence-normalizer",
3296
+ displayName: "Promotion Approval Evidence Normalizer",
3297
+ category: "release",
3298
+ runtime: {
3299
+ minVersion: "0.1.0",
3300
+ kind: "deterministic"
3301
+ },
3302
+ permissions: {
3303
+ model: false,
3304
+ network: false,
3305
+ tools: [],
3306
+ readPaths: [".agentops/requests/**", ".agentops/runs/**", "**/*.json", "**/*.md"],
3307
+ writePaths: []
3308
+ },
3309
+ inputs: ["workflowInputs", "repo", "agentResults"],
3310
+ outputs: ["summary", "metadata"],
3311
+ contextPolicy: {
3312
+ sections: ["workflowInputs", "repo", "agentResults"],
3313
+ minimalContext: true
3314
+ },
3315
+ catalog: {
3316
+ domain: "release",
3317
+ supportLevel: "internal",
3318
+ maturity: "mvp",
3319
+ trustScope: "official-core-only"
3320
+ },
3321
+ trust: {
3322
+ tier: "core",
3323
+ source: "official",
3324
+ reviewed: true
3325
+ }
3326
+ }),
3327
+ outputSchema: agentOutputSchema,
3328
+ async execute({ stateSlice }) {
3329
+ const promotionRequest = getWorkflowInput(stateSlice, "promotionRequest");
3330
+ if (!promotionRequest) {
3331
+ throw new Error("promotion-approval requires validated promotion request inputs before evidence normalization.");
3332
+ }
3333
+ const repoRoot = stateSlice.repo?.root;
3334
+ const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
3335
+ const qaReportRefs = asStringArray(intakeMetadata.qaReportRefs).length > 0
3336
+ ? asStringArray(intakeMetadata.qaReportRefs)
3337
+ : promotionRequest.qaReportRefs;
3338
+ const securityReportRefs = asStringArray(intakeMetadata.securityReportRefs).length > 0
3339
+ ? asStringArray(intakeMetadata.securityReportRefs)
3340
+ : promotionRequest.securityReportRefs;
3341
+ const releaseReportRefs = asStringArray(intakeMetadata.releaseReportRefs).length > 0
3342
+ ? asStringArray(intakeMetadata.releaseReportRefs)
3343
+ : promotionRequest.releaseReportRefs;
3344
+ const deploymentGateReportRefs = asStringArray(intakeMetadata.deploymentGateReportRefs).length > 0
3345
+ ? asStringArray(intakeMetadata.deploymentGateReportRefs)
3346
+ : promotionRequest.deploymentGateReportRefs;
3347
+ const evidenceSources = asStringArray(intakeMetadata.evidenceSources).length > 0
3348
+ ? asStringArray(intakeMetadata.evidenceSources)
3349
+ : promotionRequest.evidenceSources;
3350
+ const normalizedEvidenceSources = [
3351
+ ...new Set([...qaReportRefs, ...securityReportRefs, ...releaseReportRefs, ...deploymentGateReportRefs, ...evidenceSources])
3352
+ ];
3353
+ const missingEvidenceSources = normalizedEvidenceSources.filter((pathValue) => repoRoot && !existsSync(join(repoRoot, pathValue)));
3354
+ if (missingEvidenceSources.length > 0) {
3355
+ throw new Error(`Promotion evidence source not found: ${missingEvidenceSources[0]}`);
3356
+ }
3357
+ const referencedArtifactKinds = [
3358
+ ...new Set([...qaReportRefs, ...securityReportRefs, ...releaseReportRefs, ...deploymentGateReportRefs].flatMap((bundleRef) => repoRoot ? loadBundleArtifactKinds(join(repoRoot, bundleRef)) : []))
3359
+ ];
3360
+ const releaseReportReadiness = releaseReportRefs.map((bundleRef) => repoRoot
3361
+ ? evaluateReleaseReportReadiness(join(repoRoot, bundleRef))
3362
+ : { ready: false, detail: `Release report reference cannot be evaluated without a repository root: ${bundleRef}` });
3363
+ const deploymentGateReadiness = deploymentGateReportRefs.map((bundleRef) => repoRoot
3364
+ ? evaluateDeploymentGateApprovalReadiness(join(repoRoot, bundleRef), promotionRequest.targetEnvironment)
3365
+ : { ready: false, detail: `Deployment gate reference cannot be evaluated without a repository root: ${bundleRef}` });
3366
+ const ciEvidence = normalizeImportedCiEvidence(repoRoot, normalizedEvidenceSources);
3367
+ const ciEvidenceSummary = ciEvidence.map((entry) => summarizeCiEvidenceForRelease(entry));
3368
+ const failingChecks = ciEvidenceSummary.flatMap((entry) => entry.failingChecks);
3369
+ const verificationChecks = [
3370
+ {
3371
+ name: "qa-report-refs",
3372
+ status: qaReportRefs.length > 0 ? "passed" : "skipped",
3373
+ detail: qaReportRefs.length > 0 ? `Using ${qaReportRefs.length} validated QA report reference(s).` : "No QA report references were supplied."
3374
+ },
3375
+ {
3376
+ name: "security-report-refs",
3377
+ status: securityReportRefs.length > 0 ? "passed" : "skipped",
3378
+ detail: securityReportRefs.length > 0
3379
+ ? `Using ${securityReportRefs.length} validated security report reference(s).`
3380
+ : "No security report references were supplied."
3381
+ },
3382
+ {
3383
+ name: "release-report-refs",
3384
+ status: releaseReportRefs.length === 0
3385
+ ? "failed"
3386
+ : releaseReportReadiness.some((entry) => !entry.ready)
3387
+ ? "failed"
3388
+ : "passed",
3389
+ detail: releaseReportRefs.length > 0
3390
+ ? releaseReportReadiness.some((entry) => !entry.ready)
3391
+ ? releaseReportReadiness.filter((entry) => !entry.ready).map((entry) => entry.detail).join(" ")
3392
+ : `Using ${releaseReportRefs.length} ready release report reference(s).`
3393
+ : "At least one ready release report reference is required."
3394
+ },
3395
+ {
3396
+ name: "deployment-gate-report-refs",
3397
+ status: deploymentGateReportRefs.length === 0
3398
+ ? "failed"
3399
+ : deploymentGateReadiness.some((entry) => !entry.ready)
3400
+ ? "failed"
3401
+ : "passed",
3402
+ detail: deploymentGateReportRefs.length > 0
3403
+ ? deploymentGateReadiness.some((entry) => !entry.ready)
3404
+ ? deploymentGateReadiness.filter((entry) => !entry.ready).map((entry) => entry.detail).join(" ")
3405
+ : `Using ${deploymentGateReportRefs.length} ready deployment gate report reference(s) for ${promotionRequest.targetEnvironment}.`
3406
+ : "At least one ready deployment gate report reference is required."
3407
+ },
3408
+ {
3409
+ name: "imported-ci-evidence",
3410
+ status: ciEvidence.length === 0 ? "failed" : failingChecks.length > 0 ? "failed" : "passed",
3411
+ detail: ciEvidence.length === 0
3412
+ ? "No imported CI evidence exports were supplied."
3413
+ : failingChecks.length > 0
3414
+ ? `Imported CI evidence still reports failing checks: ${failingChecks.join(", ")}.`
3415
+ : `Using ${ciEvidence.length} imported CI evidence export(s) across ${[...new Set(ciEvidence.map((entry) => entry.pipelineName))].length} pipeline(s).`
3416
+ }
3417
+ ];
3418
+ const approvalStatus = verificationChecks.some((check) => check.status === "failed")
3419
+ ? "blocked"
3420
+ : qaReportRefs.length > 0 && securityReportRefs.length > 0
3421
+ ? "approval_recommended"
3422
+ : "needs_follow_up";
3423
+ const approvalRecommendations = [
3424
+ {
3425
+ action: "promote-release",
3426
+ classification: approvalStatus === "approval_recommended" ? "approval_required" : "deny",
3427
+ reason: approvalStatus === "approval_recommended"
3428
+ ? "Promotion remains a release-significant side effect and requires explicit maintainer approval."
3429
+ : "Keep release promotion blocked until bounded release and deployment gate evidence is complete."
3430
+ },
3431
+ {
3432
+ action: "publish-packages",
3433
+ classification: approvalStatus === "approval_recommended" ? "approval_required" : "deny",
3434
+ reason: approvalStatus === "approval_recommended"
3435
+ ? "Package publication remains outside the default read-only workflow path."
3436
+ : "Do not publish packages while promotion approval remains blocked or incomplete."
3437
+ }
3438
+ ];
3439
+ const normalization = promotionApprovalEvidenceNormalizationSchema.parse({
3440
+ qaReportRefs,
3441
+ securityReportRefs,
3442
+ releaseReportRefs,
3443
+ deploymentGateReportRefs,
3444
+ normalizedEvidenceSources,
3445
+ missingEvidenceSources: [],
3446
+ ciEvidence,
3447
+ ciEvidenceSummary,
3448
+ referencedArtifactKinds,
3449
+ verificationChecks,
3450
+ approvalRecommendations,
3451
+ approvalStatus,
3452
+ provenanceRefs: normalizedEvidenceSources
3453
+ });
3454
+ return agentOutputSchema.parse({
3455
+ summary: `Normalized promotion approval evidence across ${normalization.normalizedEvidenceSources.length} source(s) and ${normalization.ciEvidence.length} imported CI evidence export(s).`,
3456
+ findings: [],
3457
+ proposedActions: [],
3458
+ lifecycleArtifacts: [],
3459
+ requestedTools: [],
3460
+ blockedActionFlags: [],
3461
+ metadata: normalization
3462
+ });
3463
+ }
3464
+ };
3465
+ const promotionApprovalAnalystAgent = {
3466
+ manifest: agentManifestSchema.parse({
3467
+ version: 1,
3468
+ name: "promotion-approval-analyst",
3469
+ displayName: "Promotion Approval Analyst",
3470
+ category: "release",
3471
+ runtime: {
3472
+ minVersion: "0.1.0",
3473
+ kind: "reasoning"
3474
+ },
3475
+ permissions: {
3476
+ model: true,
3477
+ network: false,
3478
+ tools: [],
3479
+ readPaths: ["**/*"],
3480
+ writePaths: []
3481
+ },
3482
+ inputs: ["workflowInputs", "repo", "agentResults"],
3483
+ outputs: ["lifecycleArtifacts"],
3484
+ contextPolicy: {
3485
+ sections: ["workflowInputs", "repo", "agentResults"],
3486
+ minimalContext: true
3487
+ },
3488
+ catalog: {
3489
+ domain: "release",
3490
+ supportLevel: "internal",
3491
+ maturity: "mvp",
3492
+ trustScope: "official-core-only"
3493
+ },
3494
+ trust: {
3495
+ tier: "core",
3496
+ source: "official",
3497
+ reviewed: true
3498
+ }
3499
+ }),
3500
+ outputSchema: agentOutputSchema,
3501
+ async execute({ state, stateSlice }) {
3502
+ const promotionRequest = getWorkflowInput(stateSlice, "promotionRequest");
3503
+ const promotionIssueRefs = getWorkflowInput(stateSlice, "promotionIssueRefs") ?? [];
3504
+ const promotionScmRefs = getWorkflowInput(stateSlice, "promotionScmRefs") ?? [];
3505
+ const promotionGithubRefs = getWorkflowInput(stateSlice, "promotionGithubRefs") ?? [];
3506
+ const requestFile = getWorkflowInput(stateSlice, "requestFile");
3507
+ if (!promotionRequest) {
3508
+ throw new Error("promotion-approval requires validated promotion inputs before analysis.");
3509
+ }
3510
+ const evidenceMetadata = promotionApprovalEvidenceNormalizationSchema.safeParse(stateSlice.agentResults?.evidence?.metadata);
3511
+ const normalizedEvidence = evidenceMetadata.success ? evidenceMetadata.data : undefined;
3512
+ const evidenceSources = normalizedEvidence?.normalizedEvidenceSources && normalizedEvidence.normalizedEvidenceSources.length > 0
3513
+ ? normalizedEvidence.normalizedEvidenceSources
3514
+ : promotionRequest.evidenceSources;
3515
+ const ciEvidenceSummary = normalizedEvidence?.ciEvidenceSummary ?? [];
3516
+ const verificationChecks = normalizedEvidence?.verificationChecks ?? [];
3517
+ const referencedArtifactKinds = normalizedEvidence?.referencedArtifactKinds ?? [];
3518
+ const blockers = [
3519
+ ...verificationChecks
3520
+ .filter((check) => check.status === "failed")
3521
+ .map((check) => check.detail ?? `${check.name} failed during deterministic promotion approval review.`)
3522
+ ];
3523
+ const approvalStatus = blockers.length > 0 ? "blocked" : normalizedEvidence?.approvalStatus ?? "needs_follow_up";
3524
+ const requiredApprovals = [
3525
+ "Obtain explicit maintainer approval before any promotion or publish action.",
3526
+ `Confirm the ${promotionRequest.targetEnvironment} deployment owner accepts the current promotion window.`
3527
+ ];
3528
+ const recommendedNextSteps = [
3529
+ ...ciEvidenceSummary.map((entry) => `Review ${entry.displayLabel} (${formatCiEvidenceStatus(entry)}) before approving promotion.`),
3530
+ ...(promotionRequest.qaReportRefs.length > 0 || promotionRequest.securityReportRefs.length > 0
3531
+ ? ["Carry the validated QA and security artifacts into the promotion approval packet."]
3532
+ : ["Attach QA and security artifacts if additional assurance is required before promotion."]),
3533
+ "Keep deployment, publication, and tag creation outside this review-only workflow.",
3534
+ ...(promotionRequest.constraints.length > 0 ? [`Keep follow-up bounded by: ${promotionRequest.constraints.join("; ")}.`] : [])
3535
+ ];
3536
+ const approvalRecommendations = normalizedEvidence?.approvalRecommendations ?? [
3537
+ {
3538
+ action: "promote-release",
3539
+ classification: approvalStatus === "approval_recommended" ? "approval_required" : "deny",
3540
+ reason: approvalStatus === "approval_recommended"
3541
+ ? "Promotion remains a release-significant side effect and requires explicit maintainer approval."
3542
+ : "Keep release promotion blocked until bounded release and deployment gate evidence is complete."
3543
+ }
3544
+ ];
3545
+ const summary = `Promotion approval report prepared for ${promotionRequest.targetEnvironment}.`;
3546
+ const promotionApprovalReport = promotionApprovalArtifactSchema.parse({
3547
+ ...buildLifecycleArtifactEnvelopeBase(state, "Promotion Approval Review", summary, [requestFile ?? ".agentops/requests/promotion.yaml", ...new Set(evidenceSources)], promotionIssueRefs, promotionScmRefs, promotionGithubRefs),
3548
+ artifactKind: "promotion-approval-report",
3549
+ lifecycleDomain: "release",
3550
+ payload: {
3551
+ promotionScope: promotionRequest.promotionScope,
3552
+ targetEnvironment: promotionRequest.targetEnvironment,
3553
+ evidenceSources,
3554
+ verificationChecks,
3555
+ ciEvidenceSummary,
3556
+ approvalStatus,
3557
+ blockers,
3558
+ requiredApprovals,
3559
+ recommendedNextSteps,
3560
+ approvalRecommendations: approvalRecommendations.map((recommendation) => releaseApprovalRecommendationSchema.parse(recommendation)),
3561
+ referencedArtifactKinds,
3562
+ provenanceRefs: normalizedEvidence?.provenanceRefs ?? evidenceSources
3563
+ }
3564
+ });
3565
+ return agentOutputSchema.parse({
3566
+ summary,
3567
+ findings: [],
3568
+ proposedActions: [],
3569
+ lifecycleArtifacts: [promotionApprovalReport],
3570
+ requestedTools: [],
3571
+ blockedActionFlags: [],
3572
+ confidence: 0.78,
3573
+ metadata: {
3574
+ deterministicInputs: {
3575
+ targetEnvironment: promotionRequest.targetEnvironment,
3576
+ evidenceSources,
3577
+ ciEvidenceSummary,
3578
+ verificationChecks,
3579
+ referencedArtifactKinds,
3580
+ constraints: promotionRequest.constraints
3581
+ },
3582
+ synthesizedAssessment: {
3583
+ approvalStatus,
3584
+ blockers,
3585
+ requiredApprovals,
3586
+ recommendedNextSteps
3587
+ }
3588
+ }
3589
+ });
3590
+ }
3591
+ };
3187
3592
  const securityEvidenceNormalizationAgent = {
3188
3593
  manifest: agentManifestSchema.parse({
3189
3594
  version: 1,
@@ -4327,6 +4732,9 @@ export function createBuiltinAgentRegistry() {
4327
4732
  ["deployment-gate-intake", deploymentGateIntakeAgent],
4328
4733
  ["deployment-gate-evidence-normalizer", deploymentGateEvidenceNormalizationAgent],
4329
4734
  ["deployment-gate-analyst", deploymentGateAnalystAgent],
4735
+ ["promotion-approval-intake", promotionApprovalIntakeAgent],
4736
+ ["promotion-approval-evidence-normalizer", promotionApprovalEvidenceNormalizationAgent],
4737
+ ["promotion-approval-analyst", promotionApprovalAnalystAgent],
4330
4738
  ["security-evidence-normalizer", securityEvidenceNormalizationAgent],
4331
4739
  ["security-analyst", securityAnalystAgent],
4332
4740
  ["qa-evidence-normalizer", qaEvidenceNormalizationAgent],