From 00dbd195607fb469f31bf2d6ac14ccd73f63a5d6 Mon Sep 17 00:00:00 2001 From: Jefferson Ramos Date: Fri, 10 Jul 2026 19:53:04 -0300 Subject: [PATCH 1/2] OTA-2065: Rename Proposal API to AgenticRun in console plugin Aligns with the naming decision in OLS-3295. Renames all Proposal types, models, hooks, and file names to AgenticRun throughout the plugin codebase. Updates K8s model definitions, route paths, and i18n strings. Co-Authored-By: Claude Opus 4.6 --- ...plugin__cluster-update-console-plugin.json | 8 +- .../{proposal.test.ts => agenticrun.test.ts} | 34 +-- src/components/ClusterUpdatePage.tsx | 28 +-- .../active-plans/ActivePlansTab.tsx | 20 +- src/components/shared/PhaseIcon.tsx | 4 +- src/components/shared/PhaseLabel.tsx | 4 +- src/components/shared/SeverityLabel.tsx | 2 +- .../update-plan/AnalysisResultView.tsx | 109 +++++++--- .../update-plan/DecisionActions.tsx | 27 ++- src/components/update-plan/PlanHeader.tsx | 10 +- src/components/update-plan/UpdatePlanTab.tsx | 196 ++++++++++-------- ...seUpdateProposals.ts => useAgenticRuns.ts} | 22 +- src/hooks/useApprovalActions.ts | 71 +++---- ...eateProposal.ts => useCreateAgenticRun.ts} | 6 +- src/models/{proposal.ts => agenticrun.ts} | 70 ++++--- ...salapprovals.ts => agenticrunapprovals.ts} | 17 +- .../{proposals.ts => agenticruns.ts} | 115 +++++----- src/models/generated/analysisresults.ts | 41 ++-- src/models/generated/approvalpolicies.ts | 5 +- src/models/generated/index.ts | 5 +- 20 files changed, 442 insertions(+), 352 deletions(-) rename src/__tests__/{proposal.test.ts => agenticrun.test.ts} (88%) rename src/hooks/{useUpdateProposals.ts => useAgenticRuns.ts} (65%) rename src/hooks/{useCreateProposal.ts => useCreateAgenticRun.ts} (88%) rename src/models/{proposal.ts => agenticrun.ts} (83%) rename src/models/generated/{proposalapprovals.ts => agenticrunapprovals.ts} (94%) rename src/models/generated/{proposals.ts => agenticruns.ts} (95%) diff --git a/locales/en/plugin__cluster-update-console-plugin.json b/locales/en/plugin__cluster-update-console-plugin.json index cd6aa4d..4e579c8 100644 --- a/locales/en/plugin__cluster-update-console-plugin.json +++ b/locales/en/plugin__cluster-update-console-plugin.json @@ -24,14 +24,14 @@ "Error loading cluster version": "Error loading cluster version", "Generated": "Generated", "Lifecycle": "Lifecycle", - "Lightspeed proposals unavailable": "Lightspeed proposals unavailable", + "Lightspeed agentic runs unavailable": "Lightspeed agentic runs unavailable", "Loading": "Loading", "Minor": "Minor", "Name": "Name", "No": "No", "No active update plans": "No active update plans", + "No agentic runs available.": "No agentic runs available.", "No update history available": "No update history available", - "No update proposals available.": "No update proposals available.", "OLM operators": "OLM operators", "OLM Operators": "OLM Operators", "Operator": "Operator", @@ -48,14 +48,14 @@ "Review available versions, assess operator compatibility, and plan how this cluster version is newer OpenShift releases. Use Updates plan to prepare or start an update, Active update plans for in-flight work, and Update history for completed ones.": "Review available versions, assess operator compatibility, and plan how this cluster version is newer OpenShift releases. Use Updates plan to prepare or start an update, Active update plans for in-flight work, and Update history for completed ones.", "Sandbox: {{name}}": "Sandbox: {{name}}", "Schedule for later": "Schedule for later", - "Select proposal": "Select proposal", + "Select agentic run": "Select agentic run", "Select Update Path": "Select Update Path", "Started": "Started", "Starting analysis — waiting for agent sandbox...": "Starting analysis — waiting for agent sandbox...", "Status": "Status", "Target Version": "Target Version", "Target version {{version}} not found in available updates": "Target version {{version}} not found in available updates", - "The Lightspeed Proposal CRD is not installed on this cluster. AI-driven update planning features are disabled.": "The Lightspeed Proposal CRD is not installed on this cluster. AI-driven update planning features are disabled.", + "The Lightspeed AgenticRun CRD is not installed on this cluster. AI-driven update planning features are disabled.": "The Lightspeed AgenticRun CRD is not installed on this cluster. AI-driven update planning features are disabled.", "There are no update plans currently in progress.": "There are no update plans currently in progress.", "This cluster has no recorded update history.": "This cluster has no recorded update history.", "unknown": "unknown", diff --git a/src/__tests__/proposal.test.ts b/src/__tests__/agenticrun.test.ts similarity index 88% rename from src/__tests__/proposal.test.ts rename to src/__tests__/agenticrun.test.ts index 74fb6ac..6f04fd6 100644 --- a/src/__tests__/proposal.test.ts +++ b/src/__tests__/agenticrun.test.ts @@ -9,12 +9,12 @@ import { derivePhase, COMPONENT_TYPES, AdapterComponent, - LightspeedProposal, + LightspeedAgenticRun, LightspeedAnalysisResult, OtaReadinessSummary, OtaFinding, OtaOlmOperatorStatus, -} from '../models/proposal'; +} from '../models/agenticrun'; describe('getPhaseDisplay', () => { it('maps known phases to expected colors', () => { @@ -94,35 +94,43 @@ describe('getAnalysisDataFromResult', () => { }); describe('derivePhase', () => { - const makeProposal = (conditions: { type: string; status: string; reason?: string }[]): LightspeedProposal => + const makeAgenticRun = ( + conditions: { type: string; status: string; reason?: string }[], + ): LightspeedAgenticRun => ({ spec: { request: 'test', analysis: { agent: 'default' } }, status: { conditions }, - }) as unknown as LightspeedProposal; + }) as unknown as LightspeedAgenticRun; it('returns Pending when no conditions', () => { expect(derivePhase(undefined)).toBe('Pending'); - expect(derivePhase(makeProposal([]))).toBe('Pending'); + expect(derivePhase(makeAgenticRun([]))).toBe('Pending'); }); it('returns Analyzing when Analyzed=False', () => { - expect(derivePhase(makeProposal([{ type: 'Analyzed', status: 'False' }]))).toBe('Analyzing'); + expect(derivePhase(makeAgenticRun([{ type: 'Analyzed', status: 'False' }]))).toBe('Analyzing'); }); it('returns Failed when Analyzed=False with reason Failed', () => { - expect(derivePhase(makeProposal([{ type: 'Analyzed', status: 'False', reason: 'Failed' }]))).toBe('Failed'); + expect( + derivePhase(makeAgenticRun([{ type: 'Analyzed', status: 'False', reason: 'Failed' }])), + ).toBe('Failed'); }); it('returns Analysed when analysis-only (execution/verification skipped)', () => { - expect(derivePhase(makeProposal([ - { type: 'Analyzed', status: 'True' }, - { type: 'Executed', status: 'True', reason: 'Skipped' }, - { type: 'Verified', status: 'True', reason: 'Skipped' }, - ]))).toBe('Analysed'); + expect( + derivePhase( + makeAgenticRun([ + { type: 'Analyzed', status: 'True' }, + { type: 'Executed', status: 'True', reason: 'Skipped' }, + { type: 'Verified', status: 'True', reason: 'Skipped' }, + ]), + ), + ).toBe('Analysed'); }); it('returns Escalated when Escalated=True', () => { - expect(derivePhase(makeProposal([{ type: 'Escalated', status: 'True' }]))).toBe('Escalated'); + expect(derivePhase(makeAgenticRun([{ type: 'Escalated', status: 'True' }]))).toBe('Escalated'); }); }); diff --git a/src/components/ClusterUpdatePage.tsx b/src/components/ClusterUpdatePage.tsx index 7dda871..c653a5d 100644 --- a/src/components/ClusterUpdatePage.tsx +++ b/src/components/ClusterUpdatePage.tsx @@ -14,9 +14,9 @@ import { Title, } from '@patternfly/react-core'; import { useClusterVersion } from '../hooks/useClusterVersion'; -import { useUpdateProposals } from '../hooks/useUpdateProposals'; +import { useAgenticRuns } from '../hooks/useAgenticRuns'; import { I18N_NAMESPACE, LABELS, TERMINAL_PHASES } from '../utils/constants'; -import { LightspeedProposal, derivePhase } from '../models/proposal'; +import { LightspeedAgenticRun, derivePhase } from '../models/agenticrun'; import { ClusterVersion } from '../models/clusterversion'; import UpdatePlanTab from './update-plan/UpdatePlanTab'; import ActivePlansTab from './active-plans/ActivePlansTab'; @@ -28,20 +28,20 @@ export default function ClusterUpdatePage() { const [activeTab, setActiveTab] = React.useState(0); const [clusterVersion, cvLoaded, cvError] = useClusterVersion(); - const [proposalsRaw, proposalsLoaded, proposalsError] = useUpdateProposals(); + const [agenticRunsRaw, agenticRunsLoaded, agenticRunsError] = useAgenticRuns(); - const proposalsAvailable = proposalsLoaded && !proposalsError; - const proposals: LightspeedProposal[] = React.useMemo( + const agenticRunsAvailable = agenticRunsLoaded && !agenticRunsError; + const agenticRuns: LightspeedAgenticRun[] = React.useMemo( () => - (proposalsAvailable ? (proposalsRaw ?? []) : []).filter( - (p: LightspeedProposal) => p.metadata?.labels?.[LABELS.updateType] !== undefined, + (agenticRunsAvailable ? (agenticRunsRaw ?? []) : []).filter( + (p: LightspeedAgenticRun) => p.metadata?.labels?.[LABELS.updateType] !== undefined, ), - [proposalsAvailable, proposalsRaw], + [agenticRunsAvailable, agenticRunsRaw], ); const activePlans = React.useMemo( - () => proposals.filter((p: LightspeedProposal) => !TERMINAL_PHASES.has(derivePhase(p))), - [proposals], + () => agenticRuns.filter((p: LightspeedAgenticRun) => !TERMINAL_PHASES.has(derivePhase(p))), + [agenticRuns], ); const pageTitle = t('Cluster Update'); @@ -65,11 +65,11 @@ export default function ClusterUpdatePage() { )} - {proposalsError && ( + {agenticRunsError && ( - + {t( - 'The Lightspeed Proposal CRD is not installed on this cluster. AI-driven update planning features are disabled.', + 'The Lightspeed AgenticRun CRD is not installed on this cluster. AI-driven update planning features are disabled.', )} @@ -91,7 +91,7 @@ export default function ClusterUpdatePage() { {activeTab === 0 && ( )} diff --git a/src/components/active-plans/ActivePlansTab.tsx b/src/components/active-plans/ActivePlansTab.tsx index 3a0ff4c..d00bb61 100644 --- a/src/components/active-plans/ActivePlansTab.tsx +++ b/src/components/active-plans/ActivePlansTab.tsx @@ -5,13 +5,13 @@ import { Timestamp } from '@openshift-console/dynamic-plugin-sdk'; import { EmptyState, EmptyStateBody } from '@patternfly/react-core'; import { SearchIcon } from '@patternfly/react-icons'; import { Table, Thead, Tbody, Tr, Th, Td } from '@patternfly/react-table'; -import { LightspeedProposal, derivePhase } from '../../models/proposal'; +import { LightspeedAgenticRun, derivePhase } from '../../models/agenticrun'; import { I18N_NAMESPACE, LABELS } from '../../utils/constants'; import PhaseLabel from '../shared/PhaseLabel'; import './active-plans.css'; type ActivePlansTabProps = { - activePlans: LightspeedProposal[]; + activePlans: LightspeedAgenticRun[]; }; const ActivePlansTab: React.FC = ({ activePlans }) => { @@ -38,27 +38,27 @@ const ActivePlansTab: React.FC = ({ activePlans }) => { - {activePlans.map((proposal) => { - const targetVersion = proposal.metadata?.labels?.[LABELS.targetVersion] ?? '-'; - const updateType = proposal.metadata?.labels?.[LABELS.updateType] ?? '-'; + {activePlans.map((agenticRun) => { + const targetVersion = agenticRun.metadata?.labels?.[LABELS.targetVersion] ?? '-'; + const updateType = agenticRun.metadata?.labels?.[LABELS.updateType] ?? '-'; return ( - + - {proposal.metadata?.name} + {agenticRun.metadata?.name} {targetVersion} - + {updateType} - + ); diff --git a/src/components/shared/PhaseIcon.tsx b/src/components/shared/PhaseIcon.tsx index be261c5..9c71c69 100644 --- a/src/components/shared/PhaseIcon.tsx +++ b/src/components/shared/PhaseIcon.tsx @@ -11,10 +11,10 @@ import { QuestionCircleIcon, } from '@patternfly/react-icons'; import { Icon } from '@patternfly/react-core'; -import { ProposalPhase } from '../../models/proposal'; +import { AgenticRunPhase } from '../../models/agenticrun'; type PhaseIconProps = { - phase?: ProposalPhase | string; + phase?: AgenticRunPhase | string; }; const PhaseIcon: React.FC = ({ phase }) => { diff --git a/src/components/shared/PhaseLabel.tsx b/src/components/shared/PhaseLabel.tsx index ea038bc..688b5fb 100644 --- a/src/components/shared/PhaseLabel.tsx +++ b/src/components/shared/PhaseLabel.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; import { Label } from '@patternfly/react-core'; -import { getPhaseDisplay, ProposalPhase } from '../../models/proposal'; +import { getPhaseDisplay, AgenticRunPhase } from '../../models/agenticrun'; import PhaseIcon from './PhaseIcon'; type PhaseLabelProps = { - phase?: ProposalPhase | string; + phase?: AgenticRunPhase | string; }; const PhaseLabel: React.FC = ({ phase }) => { diff --git a/src/components/shared/SeverityLabel.tsx b/src/components/shared/SeverityLabel.tsx index a11750c..9b66d0a 100644 --- a/src/components/shared/SeverityLabel.tsx +++ b/src/components/shared/SeverityLabel.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { Label } from '@patternfly/react-core'; -import { SEVERITY_COLORS, SEVERITY_LABELS } from '../../models/proposal'; +import { SEVERITY_COLORS, SEVERITY_LABELS } from '../../models/agenticrun'; import { I18N_NAMESPACE } from '../../utils/constants'; type SeverityLabelProps = { diff --git a/src/components/update-plan/AnalysisResultView.tsx b/src/components/update-plan/AnalysisResultView.tsx index 1a26498..e88814f 100644 --- a/src/components/update-plan/AnalysisResultView.tsx +++ b/src/components/update-plan/AnalysisResultView.tsx @@ -25,7 +25,7 @@ import { sortFindings, getOlmOperatorStatus, SEVERITY_LABELS, -} from '../../models/proposal'; +} from '../../models/agenticrun'; import { I18N_NAMESPACE } from '../../utils/constants'; type AnalysisResultViewProps = { @@ -42,22 +42,42 @@ const decisionColors: Record = { const checkStatusIcon = (status: string) => { switch (status) { case 'pass': - return ; + return ( + + + + ); case 'warn': - return ; + return ( + + + + ); case 'fail': - return ; + return ( + + + + ); default: - return ; + return ( + + + + ); } }; const checkStatusColor = (status: string): 'green' | 'orange' | 'red' | 'blue' => { switch (status) { - case 'pass': return 'green'; - case 'warn': return 'orange'; - case 'fail': return 'red'; - default: return 'blue'; + case 'pass': + return 'green'; + case 'warn': + return 'orange'; + case 'fail': + return 'red'; + default: + return 'blue'; } }; @@ -80,7 +100,11 @@ const AnalysisResultView: React.FC = ({ analysisData }) {t('AI Assessment')} - @@ -104,10 +128,14 @@ const AnalysisResultView: React.FC = ({ analysisData }) {readinessSummary.checks.map((check) => ( - {check.name} + + {check.name} + {checkStatusIcon(check.status)}{' '} - + {check.detail ?? ''} @@ -125,7 +153,13 @@ const AnalysisResultView: React.FC = ({ analysisData }) {findings.map((f, i) => ( = ({ analysisData }) {olmStatus.operators.map((op) => ( - {op.displayName ?? op.name} + + {op.displayName ?? op.name} + {op.installedVersion ?? '-'} {op.channel ?? '-'} - {op.compatibleWithTarget === true && } - {op.compatibleWithTarget === false && } + {op.compatibleWithTarget === true && ( + + )} + {op.compatibleWithTarget === false && ( + + )} {op.compatibleWithTarget === undefined && '-'} - - {op.lifecycle?.supportPhase ?? '-'} - + {op.lifecycle?.supportPhase ?? '-'} ))} @@ -195,7 +237,9 @@ const AnalysisResultView: React.FC = ({ analysisData }) const decision = (legacyData.decision as string) ?? ''; const summary = (legacyData.summary as string) ?? ''; - const rawChecks = (legacyData.check_results ?? legacyData.check_by_check_analysis ?? {}) as Record; + const rawChecks = (legacyData.check_results ?? + legacyData.check_by_check_analysis ?? + {}) as Record; return ( @@ -203,7 +247,15 @@ const AnalysisResultView: React.FC = ({ analysisData }) {t('AI Assessment')} - {decision && } + {decision && ( + + )} {summary} @@ -227,12 +279,21 @@ const AnalysisResultView: React.FC = ({ analysisData }) {Object.entries(rawChecks).map(([name, check]) => ( - {name.replace(/_/g, ' ')} + + {name.replace(/_/g, ' ')} + {checkStatusIcon((check.status ?? 'unknown').toLowerCase())}{' '} - + + + + {check.reason ?? check.findings?.join('; ') ?? ''} - {check.reason ?? check.findings?.join('; ') ?? ''} ))} diff --git a/src/components/update-plan/DecisionActions.tsx b/src/components/update-plan/DecisionActions.tsx index 58be3ef..bfab79d 100644 --- a/src/components/update-plan/DecisionActions.tsx +++ b/src/components/update-plan/DecisionActions.tsx @@ -13,39 +13,38 @@ import { Label, } from '@patternfly/react-core'; import { CheckIcon } from '@patternfly/react-icons'; -import { LightspeedProposal, LightspeedProposalApproval } from '../../models/proposal'; +import { LightspeedAgenticRun, LightspeedAgenticRunApproval } from '../../models/agenticrun'; import { ClusterVersion, ClusterVersionModel } from '../../models/clusterversion'; import { I18N_NAMESPACE, LABELS } from '../../utils/constants'; import { unsanitizeVersion } from '../../utils/version'; import { getErrorMessage } from '../../utils/error'; import { useApprovalActions } from '../../hooks/useApprovalActions'; -import { useProposalApprovals } from '../../hooks/useUpdateProposals'; +import { useAgenticRunApprovals } from '../../hooks/useAgenticRuns'; type DecisionActionsProps = { - proposal: LightspeedProposal; + agenticRun: LightspeedAgenticRun; clusterVersion: ClusterVersion; }; const CONFIRM_TIMEOUT_MS = 5000; -const DecisionActions: React.FC = ({ proposal, clusterVersion }) => { +const DecisionActions: React.FC = ({ agenticRun, clusterVersion }) => { const { t } = useTranslation(I18N_NAMESPACE); const history = useHistory(); - // Find the ProposalApproval matching this proposal (same name/namespace) - const [approvals] = useProposalApprovals(); + // Find the AgenticRunApproval matching this run (same name/namespace) + const [approvals] = useAgenticRunApprovals(); const approval = React.useMemo( () => (approvals ?? []).find( - (a: LightspeedProposalApproval) => - a.metadata?.name === proposal.metadata?.name && - a.metadata?.namespace === proposal.metadata?.namespace, + (a: LightspeedAgenticRunApproval) => + a.metadata?.name === agenticRun.metadata?.name && + a.metadata?.namespace === agenticRun.metadata?.namespace, ), - [approvals, proposal.metadata?.name, proposal.metadata?.namespace], + [approvals, agenticRun.metadata?.name, agenticRun.metadata?.namespace], ); - const { approveStage, denyStage, error, clearError, inProgress } = - useApprovalActions(approval); + const { approveStage, denyStage, error, clearError, inProgress } = useApprovalActions(approval); const [confirmingApprove, setConfirmingApprove] = React.useState(false); const [confirmingDeny, setConfirmingDeny] = React.useState(false); @@ -69,7 +68,7 @@ const DecisionActions: React.FC = ({ proposal, clusterVers const approved = await approveStage('Analysis'); if (!approved) return; - const rawTarget = proposal.metadata?.labels?.[LABELS.targetVersion] ?? ''; + const rawTarget = agenticRun.metadata?.labels?.[LABELS.targetVersion] ?? ''; const targetVersion = rawTarget ? unsanitizeVersion(rawTarget) : ''; const release = clusterVersion.status?.availableUpdates?.find( (u) => u.version === targetVersion, @@ -109,7 +108,7 @@ const DecisionActions: React.FC = ({ proposal, clusterVers setConfirmingApprove(false); }, CONFIRM_TIMEOUT_MS); } - }, [confirmingApprove, approveStage, proposal, clusterVersion, history, t]); + }, [confirmingApprove, approveStage, agenticRun, clusterVersion, history, t]); const handleDenyClick = React.useCallback(async () => { if (confirmingDeny) { diff --git a/src/components/update-plan/PlanHeader.tsx b/src/components/update-plan/PlanHeader.tsx index 5a4ba4e..73ba439 100644 --- a/src/components/update-plan/PlanHeader.tsx +++ b/src/components/update-plan/PlanHeader.tsx @@ -3,25 +3,25 @@ import { useTranslation } from 'react-i18next'; import { Timestamp } from '@openshift-console/dynamic-plugin-sdk'; import { Flex, FlexItem, Label } from '@patternfly/react-core'; import { ArrowRightIcon } from '@patternfly/react-icons'; -import { LightspeedProposal } from '../../models/proposal'; +import { LightspeedAgenticRun } from '../../models/agenticrun'; import { I18N_NAMESPACE, LABELS } from '../../utils/constants'; import { getUpdateType, unsanitizeVersion } from '../../utils/version'; type PlanHeaderProps = { - proposal: LightspeedProposal; + agenticRun: LightspeedAgenticRun; }; -const PlanHeader: React.FC = ({ proposal }) => { +const PlanHeader: React.FC = ({ agenticRun }) => { const { t } = useTranslation(I18N_NAMESPACE); - const labels = proposal.metadata?.labels ?? {}; + const labels = agenticRun.metadata?.labels ?? {}; const rawCurrent = labels[LABELS.currentVersion] ?? ''; const rawTarget = labels[LABELS.targetVersion] ?? ''; const currentVersion = rawCurrent ? unsanitizeVersion(rawCurrent) : t('unknown'); const targetVersion = rawTarget ? unsanitizeVersion(rawTarget) : t('unknown'); const updateType = labels[LABELS.updateType] ?? getUpdateType(currentVersion, targetVersion); - const creationTimestamp = proposal.metadata?.creationTimestamp; + const creationTimestamp = agenticRun.metadata?.creationTimestamp; return (
diff --git a/src/components/update-plan/UpdatePlanTab.tsx b/src/components/update-plan/UpdatePlanTab.tsx index 5ca4066..f4c7625 100644 --- a/src/components/update-plan/UpdatePlanTab.tsx +++ b/src/components/update-plan/UpdatePlanTab.tsx @@ -21,31 +21,28 @@ import { RedoIcon, SearchIcon } from '@patternfly/react-icons'; import { k8sPatch } from '@openshift-console/dynamic-plugin-sdk'; import { ClusterVersion } from '../../models/clusterversion'; import { - LightspeedProposal, - LightspeedProposalModel, + LightspeedAgenticRun, + LightspeedAgenticRunModel, LightspeedAnalysisResult, - ACTIVE_PROPOSAL_PHASES, + ACTIVE_AGENTIC_RUN_PHASES, derivePhase, getAnalysisDataFromResult, getPhaseDisplay, -} from '../../models/proposal'; +} from '../../models/agenticrun'; import { I18N_NAMESPACE, LABELS } from '../../utils/constants'; import { unsanitizeVersion } from '../../utils/version'; import { useApprovalActions } from '../../hooks/useApprovalActions'; -import { - useProposalApprovals, - useAnalysisResults, -} from '../../hooks/useUpdateProposals'; +import { useAgenticRunApprovals, useAnalysisResults } from '../../hooks/useAgenticRuns'; import PhaseLabel from '../shared/PhaseLabel'; import PlanHeader from './PlanHeader'; import AnalysisResultView from './AnalysisResultView'; import DecisionActions from './DecisionActions'; type ReanalyseButtonProps = { - proposal: LightspeedProposal; + agenticRun: LightspeedAgenticRun; }; -const ReanalyseButton: React.FC = ({ proposal }) => { +const ReanalyseButton: React.FC = ({ agenticRun }) => { const { t } = useTranslation(I18N_NAMESPACE); const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(null); @@ -57,7 +54,7 @@ const ReanalyseButton: React.FC = ({ proposal }) => { setError(null); try { const timestamp = new Date().toISOString(); - const hasExisting = !!proposal.spec?.revisionFeedback; + const hasExisting = !!agenticRun.spec?.revisionFeedback; await k8sPatch({ data: [ { @@ -66,8 +63,8 @@ const ReanalyseButton: React.FC = ({ proposal }) => { value: `Re-analyse requested at ${timestamp}`, }, ], - model: LightspeedProposalModel, - resource: proposal, + model: LightspeedAgenticRunModel, + resource: agenticRun, }); } catch (err) { setError(String(err)); @@ -75,7 +72,7 @@ const ReanalyseButton: React.FC = ({ proposal }) => { setLoading(false); } }, - [proposal], + [agenticRun], ); return ( @@ -91,7 +88,13 @@ const ReanalyseButton: React.FC = ({ proposal }) => { {t('Re-analyse')} {error && ( - + {error} )} @@ -101,48 +104,46 @@ const ReanalyseButton: React.FC = ({ proposal }) => { type UpdatePlanTabProps = { clusterVersion: ClusterVersion; - proposals: LightspeedProposal[]; + agenticRuns: LightspeedAgenticRun[]; }; -const UpdatePlanTab: React.FC = ({ - clusterVersion, - proposals, -}) => { +const UpdatePlanTab: React.FC = ({ clusterVersion, agenticRuns }) => { const { t } = useTranslation(I18N_NAMESPACE); const [selectedName, setSelectedName] = React.useState(''); const [expandedPanels, setExpandedPanels] = React.useState>(new Set()); const [submittedNames, setSubmittedNames] = React.useState>(new Set()); - const [approvalsRaw] = useProposalApprovals(); + const [approvalsRaw] = useAgenticRunApprovals(); const approvals = approvalsRaw ?? []; const [analysisResultsRaw] = useAnalysisResults(); const analysisResults = analysisResultsRaw ?? []; - const selectedProposal = React.useMemo( - () => proposals.find((p) => p.metadata?.name === selectedName), - [proposals, selectedName], + const selectedRun = React.useMemo( + () => agenticRuns.find((p) => p.metadata?.name === selectedName), + [agenticRuns, selectedName], ); const selectedApproval = React.useMemo( () => approvals.find( (a) => - a.metadata?.name === selectedProposal?.metadata?.name && - a.metadata?.namespace === selectedProposal?.metadata?.namespace, + a.metadata?.name === selectedRun?.metadata?.name && + a.metadata?.namespace === selectedRun?.metadata?.namespace, ), - [approvals, selectedProposal], + [approvals, selectedRun], ); - const selectedPhase = derivePhase(selectedProposal); + const selectedPhase = derivePhase(selectedRun); - // All proposals that are active (analyzing, analyzed, or just submitted) - const activeProposals = React.useMemo( - () => proposals.filter((p) => { - const phase = derivePhase(p); - if (ACTIVE_PROPOSAL_PHASES.has(phase)) return true; - if (submittedNames.has(p.metadata?.name ?? '')) return true; - return false; - }), - [proposals, submittedNames], + // All runs that are active (analyzing, analyzed, or just submitted) + const activeRuns = React.useMemo( + () => + agenticRuns.filter((p) => { + const phase = derivePhase(p); + if (ACTIVE_AGENTIC_RUN_PHASES.has(phase)) return true; + if (submittedNames.has(p.metadata?.name ?? '')) return true; + return false; + }), + [agenticRuns, submittedNames], ); // Clear submitted tracking once the real phase kicks in @@ -150,43 +151,43 @@ const UpdatePlanTab: React.FC = ({ if (submittedNames.size === 0) return; const stillPending = new Set(); submittedNames.forEach((name) => { - const p = proposals.find((pr) => pr.metadata?.name === name); + const p = agenticRuns.find((pr) => pr.metadata?.name === name); if (p && derivePhase(p) === 'Pending') stillPending.add(name); }); if (stillPending.size < submittedNames.size) setSubmittedNames(stillPending); - }, [proposals, submittedNames]); + }, [agenticRuns, submittedNames]); - // Auto-expand newly analysed proposals + // Auto-expand newly analysed runs React.useEffect(() => { - if (activeProposals.length > 0) { + if (activeRuns.length > 0) { setExpandedPanels((prev) => { const next = new Set(prev); - activeProposals.forEach((p) => { + activeRuns.forEach((p) => { if (p.metadata?.name) next.add(p.metadata.name); }); return next; }); } - }, [activeProposals]); + }, [activeRuns]); const { approveStage, error: approveError, inProgress } = useApprovalActions(selectedApproval); const handleAnalyse = React.useCallback(async () => { - if (!selectedProposal?.metadata?.name) return; - const name = selectedProposal.metadata.name; + if (!selectedRun?.metadata?.name) return; + const name = selectedRun.metadata.name; const ok = await approveStage('Analysis'); if (ok) { setSubmittedNames((prev) => new Set(prev).add(name)); setExpandedPanels((prev) => new Set(prev).add(name)); } - }, [selectedProposal, approveStage]); + }, [selectedRun, approveStage]); - // Auto-select first proposal if none selected + // Auto-select first run if none selected React.useEffect(() => { - if (!selectedName && proposals.length > 0) { - setSelectedName(proposals[0].metadata?.name ?? ''); + if (!selectedName && agenticRuns.length > 0) { + setSelectedName(agenticRuns[0].metadata?.name ?? ''); } - }, [selectedName, proposals]); + }, [selectedName, agenticRuns]); const togglePanel = React.useCallback((name: string) => { setExpandedPanels((prev) => { @@ -200,17 +201,15 @@ const UpdatePlanTab: React.FC = ({ }); }, []); - if (proposals.length === 0) { - return ( - {t('No update proposals available.')} - ); + if (agenticRuns.length === 0) { + return {t('No agentic runs available.')}; } const showAnalyseButton = selectedPhase === 'Pending'; return ( - {/* Proposal selector */} + {/* Run selector */} {t('Select Update Path')} @@ -220,14 +219,17 @@ const UpdatePlanTab: React.FC = ({ setSelectedName(value)} - aria-label={t('Select proposal')} + aria-label={t('Select agentic run')} > - {proposals.map((p) => { + {agenticRuns.map((p) => { const rawTarget = p.metadata?.labels?.[LABELS.targetVersion] ?? ''; - const target = rawTarget ? unsanitizeVersion(rawTarget) : p.metadata?.name ?? ''; + const target = rawTarget + ? unsanitizeVersion(rawTarget) + : (p.metadata?.name ?? ''); const updateType = p.metadata?.labels?.[LABELS.updateType] ?? ''; const pPhase = derivePhase(p); - const suffix = pPhase !== 'Pending' ? ` (${getPhaseDisplay(pPhase).label})` : ''; + const suffix = + pPhase !== 'Pending' ? ` (${getPhaseDisplay(pPhase).label})` : ''; return ( = ({ )} {approveError && ( - + {approveError} )} @@ -264,26 +272,30 @@ const UpdatePlanTab: React.FC = ({ - {/* Analysed proposals as expandable panels */} - {activeProposals.map((proposal) => { - const name = proposal.metadata?.name ?? ''; - const rawTarget = proposal.metadata?.labels?.[LABELS.targetVersion] ?? ''; + {/* Analysed runs as expandable panels */} + {activeRuns.map((agenticRun) => { + const name = agenticRun.metadata?.name ?? ''; + const rawTarget = agenticRun.metadata?.labels?.[LABELS.targetVersion] ?? ''; const target = rawTarget ? unsanitizeVersion(rawTarget) : name; - const pPhase = derivePhase(proposal); + const pPhase = derivePhase(agenticRun); const phaseDisplay = getPhaseDisplay(pPhase); - const resultRef = (proposal.status?.steps?.analysis?.results?.[0] as { name?: string })?.name; + const resultRef = (agenticRun.status?.steps?.analysis?.results?.[0] as { name?: string }) + ?.name; const result = resultRef ? analysisResults.find( (r: LightspeedAnalysisResult) => r.metadata?.name === resultRef && - r.metadata?.namespace === proposal.metadata?.namespace, + r.metadata?.namespace === agenticRun.metadata?.namespace, ) : undefined; const resultData = getAnalysisDataFromResult(result); - const readinessSummary = resultData.components.find((c) => c.type === 'ota_readiness_summary'); - const decision = (readinessSummary as Record)?.decision as string | undefined - ?? resultData.analysisData?.decision as string | undefined; + const readinessSummary = resultData.components.find( + (c) => c.type === 'ota_readiness_summary', + ); + const decision = + ((readinessSummary as Record)?.decision as string | undefined) ?? + (resultData.analysisData?.decision as string | undefined); return ( @@ -294,12 +306,22 @@ const UpdatePlanTab: React.FC = ({ {t('Update to {{version}}', { version: target })} - + {decision && ( )} - + } @@ -317,13 +339,16 @@ const UpdatePlanTab: React.FC = ({ > - + - {(pPhase === 'Analyzing' || (pPhase === 'Pending' && submittedNames.has(name))) ? ( + {pPhase === 'Analyzing' || (pPhase === 'Pending' && submittedNames.has(name)) ? ( - + @@ -331,18 +356,20 @@ const UpdatePlanTab: React.FC = ({ - {proposal.status?.steps?.analysis?.sandbox?.claimName + {agenticRun.status?.steps?.analysis?.sandbox?.claimName ? t('AI agent is analysing cluster readiness...') : t('Starting analysis — waiting for agent sandbox...')} - {proposal.status?.steps?.analysis?.sandbox?.claimName && ( + {agenticRun.status?.steps?.analysis?.sandbox?.claimName && ( - {t('Sandbox: {{name}}', { name: proposal.status.steps.analysis.sandbox.claimName })} + {t('Sandbox: {{name}}', { + name: agenticRun.status.steps.analysis.sandbox.claimName, + })} {' — '} @@ -360,21 +387,22 @@ const UpdatePlanTab: React.FC = ({ ) : pPhase === 'Failed' ? ( - {(proposal.status?.conditions as { type: string; message: string }[]) - ?.find((c) => c.type === 'Analyzed')?.message ?? t('Unknown error')} + {(agenticRun.status?.conditions as { type: string; message: string }[])?.find( + (c) => c.type === 'Analyzed', + )?.message ?? t('Unknown error')} ) : ( <> - {(resultData.components.length > 0 || resultData.analysisData) ? ( + {resultData.components.length > 0 || resultData.analysisData ? ( ) : ( {t('Analysis result not yet available.')} )} - + )} diff --git a/src/hooks/useUpdateProposals.ts b/src/hooks/useAgenticRuns.ts similarity index 65% rename from src/hooks/useUpdateProposals.ts rename to src/hooks/useAgenticRuns.ts index 3e6761b..7a8ad0c 100644 --- a/src/hooks/useUpdateProposals.ts +++ b/src/hooks/useAgenticRuns.ts @@ -1,28 +1,28 @@ import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk'; import { - LightspeedProposal, - LightspeedProposalApproval, + LightspeedAgenticRun, + LightspeedAgenticRunApproval, LightspeedApprovalPolicy, LightspeedAnalysisResult, - LightspeedProposalGVK, - ProposalApprovalGVK, + LightspeedAgenticRunGVK, + AgenticRunApprovalGVK, ApprovalPolicyGVK, AnalysisResultGVK, -} from '../models/proposal'; +} from '../models/agenticrun'; import { LIGHTSPEED_NAMESPACE } from '../utils/constants'; -export const useUpdateProposals = () => { - return useK8sWatchResource({ - groupVersionKind: LightspeedProposalGVK, +export const useAgenticRuns = () => { + return useK8sWatchResource({ + groupVersionKind: LightspeedAgenticRunGVK, isList: true, namespaced: true, namespace: LIGHTSPEED_NAMESPACE, }); }; -export const useProposalApprovals = () => { - return useK8sWatchResource({ - groupVersionKind: ProposalApprovalGVK, +export const useAgenticRunApprovals = () => { + return useK8sWatchResource({ + groupVersionKind: AgenticRunApprovalGVK, isList: true, namespaced: true, namespace: LIGHTSPEED_NAMESPACE, diff --git a/src/hooks/useApprovalActions.ts b/src/hooks/useApprovalActions.ts index 980e56a..39a49a6 100644 --- a/src/hooks/useApprovalActions.ts +++ b/src/hooks/useApprovalActions.ts @@ -1,14 +1,11 @@ import * as React from 'react'; import { k8sPatch } from '@openshift-console/dynamic-plugin-sdk'; -import { - LightspeedProposalApproval, - ProposalApprovalModel, -} from '../models/proposal'; +import { LightspeedAgenticRunApproval, AgenticRunApprovalModel } from '../models/agenticrun'; import { getErrorMessage } from '../utils/error'; export type ApprovalStageType = 'Analysis' | 'Execution' | 'Verification' | 'Escalation'; -export const useApprovalActions = (approval?: LightspeedProposalApproval) => { +export const useApprovalActions = (approval?: LightspeedAgenticRunApproval) => { const [inProgress, setInProgress] = React.useState(false); const [error, setError] = React.useState(null); @@ -16,7 +13,10 @@ export const useApprovalActions = (approval?: LightspeedProposalApproval) => { approvalRef.current = approval; const approveStage = React.useCallback( - async (stageType: ApprovalStageType, options?: { optionIndex?: number; maxAttempts?: number }) => { + async ( + stageType: ApprovalStageType, + options?: { optionIndex?: number; maxAttempts?: number }, + ) => { const currentApproval = approvalRef.current; if (!currentApproval) return false; setInProgress(true); @@ -51,7 +51,7 @@ export const useApprovalActions = (approval?: LightspeedProposalApproval) => { ? { op: 'replace', path: '/spec/stages', value: newStages } : { op: 'add', path: '/spec', value: { stages: newStages } }, ], - model: ProposalApprovalModel, + model: AgenticRunApprovalModel, resource: currentApproval, }); return true; @@ -65,37 +65,34 @@ export const useApprovalActions = (approval?: LightspeedProposalApproval) => { [], ); - const denyStage = React.useCallback( - async (stageType: ApprovalStageType) => { - const currentApproval = approvalRef.current; - if (!currentApproval) return false; - setInProgress(true); - setError(null); - try { - const existingStages = currentApproval.spec?.stages ?? []; - const stageEntry = { type: stageType, decision: 'Denied' }; - const newStages = [...existingStages, stageEntry]; - const hasSpec = currentApproval.spec?.stages !== undefined; + const denyStage = React.useCallback(async (stageType: ApprovalStageType) => { + const currentApproval = approvalRef.current; + if (!currentApproval) return false; + setInProgress(true); + setError(null); + try { + const existingStages = currentApproval.spec?.stages ?? []; + const stageEntry = { type: stageType, decision: 'Denied' }; + const newStages = [...existingStages, stageEntry]; + const hasSpec = currentApproval.spec?.stages !== undefined; - await k8sPatch({ - data: [ - hasSpec - ? { op: 'replace', path: '/spec/stages', value: newStages } - : { op: 'add', path: '/spec', value: { stages: newStages } }, - ], - model: ProposalApprovalModel, - resource: currentApproval, - }); - return true; - } catch (err) { - setError(getErrorMessage(err)); - return false; - } finally { - setInProgress(false); - } - }, - [], - ); + await k8sPatch({ + data: [ + hasSpec + ? { op: 'replace', path: '/spec/stages', value: newStages } + : { op: 'add', path: '/spec', value: { stages: newStages } }, + ], + model: AgenticRunApprovalModel, + resource: currentApproval, + }); + return true; + } catch (err) { + setError(getErrorMessage(err)); + return false; + } finally { + setInProgress(false); + } + }, []); return { approveStage, diff --git a/src/hooks/useCreateProposal.ts b/src/hooks/useCreateAgenticRun.ts similarity index 88% rename from src/hooks/useCreateProposal.ts rename to src/hooks/useCreateAgenticRun.ts index ca0c66b..006310c 100644 --- a/src/hooks/useCreateProposal.ts +++ b/src/hooks/useCreateAgenticRun.ts @@ -4,11 +4,11 @@ import { ClusterVersion, ClusterVersionModel } from '../models/clusterversion'; import { ANNOTATIONS } from '../utils/constants'; import { getErrorMessage } from '../utils/error'; -export const useCreateProposal = () => { +export const useCreateAgenticRun = () => { const [creating, setCreating] = React.useState(false); const [error, setError] = React.useState(null); - const requestProposal = React.useCallback( + const requestAgenticRun = React.useCallback( async (clusterVersion: ClusterVersion, targetVersion: string) => { setCreating(true); setError(null); @@ -37,5 +37,5 @@ export const useCreateProposal = () => { const clearError = React.useCallback(() => setError(null), []); - return { requestProposal, creating, error, clearError }; + return { requestAgenticRun, creating, error, clearError }; }; diff --git a/src/models/proposal.ts b/src/models/agenticrun.ts similarity index 83% rename from src/models/proposal.ts rename to src/models/agenticrun.ts index 0e98576..f5748d8 100644 --- a/src/models/proposal.ts +++ b/src/models/agenticrun.ts @@ -4,33 +4,33 @@ import { K8sResourceCondition, getGroupVersionKindForModel, } from '@openshift-console/dynamic-plugin-sdk'; -import { ProposalSpec, ProposalStatus } from './generated/proposals'; -import { ProposalApprovalSpec, ProposalApprovalStatus } from './generated/proposalapprovals'; +import { AgenticRunSpec, AgenticRunStatus } from './generated/agenticruns'; +import { AgenticRunApprovalSpec, AgenticRunApprovalStatus } from './generated/agenticrunapprovals'; import { ApprovalPolicySpec } from './generated/approvalpolicies'; import { AnalysisResultSpec, AnalysisResultStatus } from './generated/analysisresults'; // --- K8s Models --- -export const LightspeedProposalModel: K8sModel = { +export const LightspeedAgenticRunModel: K8sModel = { apiGroup: 'agentic.openshift.io', apiVersion: 'v1alpha1', - kind: 'Proposal', - plural: 'proposals', - abbr: 'LSP', + kind: 'AgenticRun', + plural: 'agenticruns', + abbr: 'LSAR', namespaced: true, - label: 'Proposal', - labelPlural: 'Proposals', + label: 'AgenticRun', + labelPlural: 'AgenticRuns', }; -export const ProposalApprovalModel: K8sModel = { +export const AgenticRunApprovalModel: K8sModel = { apiGroup: 'agentic.openshift.io', apiVersion: 'v1alpha1', - kind: 'ProposalApproval', - plural: 'proposalapprovals', + kind: 'AgenticRunApproval', + plural: 'agenticrunapprovals', abbr: 'PA', namespaced: true, - label: 'ProposalApproval', - labelPlural: 'ProposalApprovals', + label: 'AgenticRunApproval', + labelPlural: 'AgenticRunApprovals', }; export const AnalysisResultModel: K8sModel = { @@ -55,21 +55,21 @@ export const ApprovalPolicyModel: K8sModel = { labelPlural: 'ApprovalPolicies', }; -export const LightspeedProposalGVK = getGroupVersionKindForModel(LightspeedProposalModel); -export const ProposalApprovalGVK = getGroupVersionKindForModel(ProposalApprovalModel); +export const LightspeedAgenticRunGVK = getGroupVersionKindForModel(LightspeedAgenticRunModel); +export const AgenticRunApprovalGVK = getGroupVersionKindForModel(AgenticRunApprovalModel); export const AnalysisResultGVK = getGroupVersionKindForModel(AnalysisResultModel); export const ApprovalPolicyGVK = getGroupVersionKindForModel(ApprovalPolicyModel); // --- Resource Types (composed from generated CRD types + K8sResourceCommon) --- -export type LightspeedProposal = K8sResourceCommon & { - spec: ProposalSpec; - status?: ProposalStatus; +export type LightspeedAgenticRun = K8sResourceCommon & { + spec: AgenticRunSpec; + status?: AgenticRunStatus; }; -export type LightspeedProposalApproval = K8sResourceCommon & { - spec?: ProposalApprovalSpec; - status?: ProposalApprovalStatus; +export type LightspeedAgenticRunApproval = K8sResourceCommon & { + spec?: AgenticRunApprovalSpec; + status?: AgenticRunApprovalStatus; }; export type LightspeedAnalysisResult = K8sResourceCommon & { @@ -84,7 +84,7 @@ export type LightspeedApprovalPolicy = K8sResourceCommon & { // --- Phase derivation --- // The CRD has no status.phase field. Derive it from status.conditions. -export type ProposalPhase = +export type AgenticRunPhase = | 'Pending' | 'Analyzing' | 'Analysed' @@ -98,14 +98,18 @@ export type ProposalPhase = | 'Failed' | 'Escalated'; -export const ACTIVE_PROPOSAL_PHASES = new Set([ - 'Analyzing', 'Analysed', 'Proposed', 'Completed', 'Escalated', 'Failed', +export const ACTIVE_AGENTIC_RUN_PHASES = new Set([ + 'Analyzing', + 'Analysed', + 'Proposed', + 'Completed', + 'Escalated', + 'Failed', ]); -export const derivePhase = (proposal?: LightspeedProposal): ProposalPhase => { - const conditions = proposal?.status?.conditions ?? []; - const find = (type: string) => - conditions.find((c: K8sResourceCondition) => c.type === type); +export const derivePhase = (agenticRun?: LightspeedAgenticRun): AgenticRunPhase => { + const conditions = agenticRun?.status?.conditions ?? []; + const find = (type: string) => conditions.find((c: K8sResourceCondition) => c.type === type); const escalated = find('Escalated'); if (escalated?.status === 'True') return 'Escalated'; @@ -114,7 +118,7 @@ export const derivePhase = (proposal?: LightspeedProposal): ProposalPhase => { const executed = find('Executed'); const analyzed = find('Analyzed'); - // Analysis-only proposals: execution and verification are Skipped + // Analysis-only runs: execution and verification are Skipped const executionSkipped = executed?.reason === 'Skipped'; const verificationSkipped = verified?.reason === 'Skipped'; if (analyzed?.status === 'True' && executionSkipped && verificationSkipped) { @@ -136,7 +140,7 @@ export const derivePhase = (proposal?: LightspeedProposal): ProposalPhase => { if (analyzed?.status === 'False') return 'Analyzing'; // Check if analysis step has any results (in progress) - if (proposal?.status?.steps?.analysis?.results?.length) return 'Analyzing'; + if (agenticRun?.status?.steps?.analysis?.results?.length) return 'Analyzing'; // If any condition is Unknown, the operator is still reconciling if (conditions.some((c: K8sResourceCondition) => c.status === 'Unknown')) return 'Pending'; @@ -151,7 +155,7 @@ export type PhaseDisplay = { label: string; }; -export const getPhaseDisplay = (phase?: ProposalPhase | string): PhaseDisplay => { +export const getPhaseDisplay = (phase?: AgenticRunPhase | string): PhaseDisplay => { switch (phase) { case 'Pending': return { color: 'grey', label: 'Pending' }; @@ -211,9 +215,7 @@ export type AnalysisData = { analysisData?: AnalysisDataPayload; }; -export const getAnalysisDataFromResult = ( - result?: LightspeedAnalysisResult, -): AnalysisData => { +export const getAnalysisDataFromResult = (result?: LightspeedAnalysisResult): AnalysisData => { if (!result?.status?.options?.length) { return { components: [] }; } diff --git a/src/models/generated/proposalapprovals.ts b/src/models/generated/agenticrunapprovals.ts similarity index 94% rename from src/models/generated/proposalapprovals.ts rename to src/models/generated/agenticrunapprovals.ts index 298d136..da2ad1b 100644 --- a/src/models/generated/proposalapprovals.ts +++ b/src/models/generated/agenticrunapprovals.ts @@ -1,10 +1,9 @@ // Auto-generated from CRD — do not edit manually. // Regenerate with: make generate-types - -export type ProposalApprovalSpec = { +export type AgenticRunApprovalSpec = { /** stages lists the approved (or denied) workflow steps. Each entry is a discriminated union keyed b... */ - stages?: ({ + stages?: { /** analysis contains approval parameters for the analysis step. Required when type is Analysis. */ analysis?: { /** agent is the Agent CR for this step. Defaults to "default". */ @@ -33,14 +32,14 @@ export type ProposalApprovalSpec = { /** agent is the Agent CR for this step. Defaults to "default". */ agent?: string; }; - })[]; + }[]; }; -export type ProposalApprovalStatus = { +export type AgenticRunApprovalStatus = { /** stages contains the per-stage approval status set by the controller. */ - stages?: ({ + stages?: { /** conditions for this approval stage. */ - conditions?: ({ + conditions?: { /** lastTransitionTime is the last time the condition transitioned from one status to another. This s... */ lastTransitionTime: string; /** message is a human readable message indicating details about the transition. This may be an empty... */ @@ -53,8 +52,8 @@ export type ProposalApprovalStatus = { status: 'True' | 'False' | 'Unknown'; /** type of condition in CamelCase or in foo.example.com/CamelCase. */ type: string; - })[]; + }[]; /** name identifies the workflow step. */ name: string; - })[]; + }[]; }; diff --git a/src/models/generated/proposals.ts b/src/models/generated/agenticruns.ts similarity index 95% rename from src/models/generated/proposals.ts rename to src/models/generated/agenticruns.ts index 7af9d4a..bf69364 100644 --- a/src/models/generated/proposals.ts +++ b/src/models/generated/agenticruns.ts @@ -1,8 +1,7 @@ // Auto-generated from CRD — do not edit manually. // Regenerate with: make generate-types - -export type ProposalSpec = { +export type AgenticRunSpec = { /** analysis defines per-step configuration for the analysis step, including which agent handles it a... */ analysis: { /** agent is the name of the cluster-scoped Agent CR to use for this step. Defaults to "default" when... */ @@ -10,9 +9,9 @@ export type ProposalSpec = { /** tools provides per-step tools that replace the shared spec.tools for this step. Use this when dif... */ tools?: { /** mcpServers defines external MCP (Model Context Protocol) servers the agent can connect to for add... */ - mcpServers?: ({ + mcpServers?: { /** headers to send to the MCP server. Maximum 20 items. */ - headers?: ({ + headers?: { /** name of the header (e.g., "Authorization", "X-API-Key"). Must be at least 1 character, containing... */ name: string; /** valueFrom is the source of the header value. */ @@ -25,16 +24,16 @@ export type ProposalSpec = { /** type specifies the source type for the header value. Allowed values: - "Secret" — reads the... */ type: 'Secret' | 'ServiceAccountToken' | 'Client'; }; - })[]; + }[]; /** name of the MCP server. Must start with a letter and contain only lowercase alphanumeric characte... */ name: string; /** timeoutSeconds is the per-request timeout for calls to this MCP server, in seconds. Default is 5.... */ timeoutSeconds?: number; /** url of the MCP server (HTTP/HTTPS). Must be an HTTP or HTTPS URL, maximum 2048 characters. */ url: string; - })[]; + }[]; /** requiredSecrets declares Kubernetes Secrets that the sandbox pod needs at runtime. The cluster ad... */ - requiredSecrets?: ({ + requiredSecrets?: { /** description explains what this secret is used for, helping the cluster admin understand what cred... */ description?: string; /** mountAs specifies how the secret is exposed in the sandbox pod. */ @@ -54,14 +53,14 @@ export type ProposalSpec = { }; /** name of the Secret (must exist in the operator namespace). Must be a valid RFC 1123 DNS subdomain. */ name: string; - })[]; + }[]; /** skills defines one or more OCI images containing skills to mount in the agent's sandbox pod. The ... */ - skills?: ({ + skills?: { /** image is the OCI image reference containing skills. The operator mounts this as a Kubernetes imag... */ image: string; /** paths specifies which directories from the image are mounted. Each path is mounted as a separate ... */ - paths: (string)[]; - })[]; + paths: string[]; + }[]; }; }; /** analysisOutput configures the analysis step's structured output. The mode field controls which bu... */ @@ -78,9 +77,9 @@ export type ProposalSpec = { /** tools provides per-step tools that replace the shared spec.tools for this step. Use this when dif... */ tools?: { /** mcpServers defines external MCP (Model Context Protocol) servers the agent can connect to for add... */ - mcpServers?: ({ + mcpServers?: { /** headers to send to the MCP server. Maximum 20 items. */ - headers?: ({ + headers?: { /** name of the header (e.g., "Authorization", "X-API-Key"). Must be at least 1 character, containing... */ name: string; /** valueFrom is the source of the header value. */ @@ -93,16 +92,16 @@ export type ProposalSpec = { /** type specifies the source type for the header value. Allowed values: - "Secret" — reads the... */ type: 'Secret' | 'ServiceAccountToken' | 'Client'; }; - })[]; + }[]; /** name of the MCP server. Must start with a letter and contain only lowercase alphanumeric characte... */ name: string; /** timeoutSeconds is the per-request timeout for calls to this MCP server, in seconds. Default is 5.... */ timeoutSeconds?: number; /** url of the MCP server (HTTP/HTTPS). Must be an HTTP or HTTPS URL, maximum 2048 characters. */ url: string; - })[]; + }[]; /** requiredSecrets declares Kubernetes Secrets that the sandbox pod needs at runtime. The cluster ad... */ - requiredSecrets?: ({ + requiredSecrets?: { /** description explains what this secret is used for, helping the cluster admin understand what cred... */ description?: string; /** mountAs specifies how the secret is exposed in the sandbox pod. */ @@ -122,14 +121,14 @@ export type ProposalSpec = { }; /** name of the Secret (must exist in the operator namespace). Must be a valid RFC 1123 DNS subdomain. */ name: string; - })[]; + }[]; /** skills defines one or more OCI images containing skills to mount in the agent's sandbox pod. The ... */ - skills?: ({ + skills?: { /** image is the OCI image reference containing skills. The operator mounts this as a Kubernetes imag... */ image: string; /** paths specifies which directories from the image are mounted. Each path is mounted as a separate ... */ - paths: (string)[]; - })[]; + paths: string[]; + }[]; }; }; /** request is the user's original request, alert description, or a description of what triggered thi... */ @@ -137,13 +136,13 @@ export type ProposalSpec = { /** revisionFeedback is the user's free-text feedback requesting changes to the analysis. Patching th... */ revisionFeedback?: string; /** targetNamespaces are the Kubernetes namespace(s) this proposal operates on. Used for RBAC scoping... */ - targetNamespaces?: (string)[]; + targetNamespaces?: string[]; /** tools defines the default tools for all steps: skills images, MCP servers, and required secrets. ... */ tools?: { /** mcpServers defines external MCP (Model Context Protocol) servers the agent can connect to for add... */ - mcpServers?: ({ + mcpServers?: { /** headers to send to the MCP server. Maximum 20 items. */ - headers?: ({ + headers?: { /** name of the header (e.g., "Authorization", "X-API-Key"). Must be at least 1 character, containing... */ name: string; /** valueFrom is the source of the header value. */ @@ -156,16 +155,16 @@ export type ProposalSpec = { /** type specifies the source type for the header value. Allowed values: - "Secret" — reads the... */ type: 'Secret' | 'ServiceAccountToken' | 'Client'; }; - })[]; + }[]; /** name of the MCP server. Must start with a letter and contain only lowercase alphanumeric characte... */ name: string; /** timeoutSeconds is the per-request timeout for calls to this MCP server, in seconds. Default is 5.... */ timeoutSeconds?: number; /** url of the MCP server (HTTP/HTTPS). Must be an HTTP or HTTPS URL, maximum 2048 characters. */ url: string; - })[]; + }[]; /** requiredSecrets declares Kubernetes Secrets that the sandbox pod needs at runtime. The cluster ad... */ - requiredSecrets?: ({ + requiredSecrets?: { /** description explains what this secret is used for, helping the cluster admin understand what cred... */ description?: string; /** mountAs specifies how the secret is exposed in the sandbox pod. */ @@ -185,14 +184,14 @@ export type ProposalSpec = { }; /** name of the Secret (must exist in the operator namespace). Must be a valid RFC 1123 DNS subdomain. */ name: string; - })[]; + }[]; /** skills defines one or more OCI images containing skills to mount in the agent's sandbox pod. The ... */ - skills?: ({ + skills?: { /** image is the OCI image reference containing skills. The operator mounts this as a Kubernetes imag... */ image: string; /** paths specifies which directories from the image are mounted. Each path is mounted as a separate ... */ - paths: (string)[]; - })[]; + paths: string[]; + }[]; }; /** verification defines per-step configuration for the verification step. Omit to skip verification.... */ verification?: { @@ -201,9 +200,9 @@ export type ProposalSpec = { /** tools provides per-step tools that replace the shared spec.tools for this step. Use this when dif... */ tools?: { /** mcpServers defines external MCP (Model Context Protocol) servers the agent can connect to for add... */ - mcpServers?: ({ + mcpServers?: { /** headers to send to the MCP server. Maximum 20 items. */ - headers?: ({ + headers?: { /** name of the header (e.g., "Authorization", "X-API-Key"). Must be at least 1 character, containing... */ name: string; /** valueFrom is the source of the header value. */ @@ -216,16 +215,16 @@ export type ProposalSpec = { /** type specifies the source type for the header value. Allowed values: - "Secret" — reads the... */ type: 'Secret' | 'ServiceAccountToken' | 'Client'; }; - })[]; + }[]; /** name of the MCP server. Must start with a letter and contain only lowercase alphanumeric characte... */ name: string; /** timeoutSeconds is the per-request timeout for calls to this MCP server, in seconds. Default is 5.... */ timeoutSeconds?: number; /** url of the MCP server (HTTP/HTTPS). Must be an HTTP or HTTPS URL, maximum 2048 characters. */ url: string; - })[]; + }[]; /** requiredSecrets declares Kubernetes Secrets that the sandbox pod needs at runtime. The cluster ad... */ - requiredSecrets?: ({ + requiredSecrets?: { /** description explains what this secret is used for, helping the cluster admin understand what cred... */ description?: string; /** mountAs specifies how the secret is exposed in the sandbox pod. */ @@ -245,21 +244,21 @@ export type ProposalSpec = { }; /** name of the Secret (must exist in the operator namespace). Must be a valid RFC 1123 DNS subdomain. */ name: string; - })[]; + }[]; /** skills defines one or more OCI images containing skills to mount in the agent's sandbox pod. The ... */ - skills?: ({ + skills?: { /** image is the OCI image reference containing skills. The operator mounts this as a Kubernetes imag... */ image: string; /** paths specifies which directories from the image are mounted. Each path is mounted as a separate ... */ - paths: (string)[]; - })[]; + paths: string[]; + }[]; }; }; }; -export type ProposalStatus = { +export type AgenticRunStatus = { /** conditions represent the latest available observations using the standard Kubernetes condition pa... */ - conditions?: ({ + conditions?: { /** lastTransitionTime is the last time the condition transitioned from one status to another. This s... */ lastTransitionTime: string; /** message is a human readable message indicating details about the transition. This may be an empty... */ @@ -272,13 +271,13 @@ export type ProposalStatus = { status: 'True' | 'False' | 'Unknown'; /** type of condition in CamelCase or in foo.example.com/CamelCase. */ type: string; - })[]; + }[]; /** steps contains the per-step observed state (analysis, execution, verification). Each step indepen... */ steps?: { /** analysis is the observed state of the analysis step. */ analysis?: { /** conditions for this step. */ - conditions?: ({ + conditions?: { /** lastTransitionTime is the last time the condition transitioned from one status to another. This s... */ lastTransitionTime: string; /** message is a human readable message indicating details about the transition. This may be an empty... */ @@ -291,14 +290,14 @@ export type ProposalStatus = { status: 'True' | 'False' | 'Unknown'; /** type of condition in CamelCase or in foo.example.com/CamelCase. */ type: string; - })[]; + }[]; /** results references AnalysisResult CRs, newest last. Each entry corresponds to one analysis attempt. */ - results?: ({ + results?: { /** name is the name of the result CR. */ name: string; /** outcome indicates the result of this step attempt. Must be one of: Succeeded, Failed. */ outcome: 'Succeeded' | 'Failed'; - })[]; + }[]; /** sandbox tracks the sandbox used. */ sandbox?: { /** claimName is the name of the SandboxClaim resource that owns the sandbox pod. Maximum 253 charact... */ @@ -310,7 +309,7 @@ export type ProposalStatus = { /** escalation is the observed state of the escalation step. */ escalation?: { /** conditions for this step. */ - conditions?: ({ + conditions?: { /** lastTransitionTime is the last time the condition transitioned from one status to another. This s... */ lastTransitionTime: string; /** message is a human readable message indicating details about the transition. This may be an empty... */ @@ -323,14 +322,14 @@ export type ProposalStatus = { status: 'True' | 'False' | 'Unknown'; /** type of condition in CamelCase or in foo.example.com/CamelCase. */ type: string; - })[]; + }[]; /** results references EscalationResult CRs, newest last. */ - results?: ({ + results?: { /** name is the name of the result CR. */ name: string; /** outcome indicates the result of this step attempt. Must be one of: Succeeded, Failed. */ outcome: 'Succeeded' | 'Failed'; - })[]; + }[]; /** sandbox tracks the sandbox used. */ sandbox?: { /** claimName is the name of the SandboxClaim resource that owns the sandbox pod. Maximum 253 charact... */ @@ -342,7 +341,7 @@ export type ProposalStatus = { /** execution is the observed state of the execution step. */ execution?: { /** conditions for this step. */ - conditions?: ({ + conditions?: { /** lastTransitionTime is the last time the condition transitioned from one status to another. This s... */ lastTransitionTime: string; /** message is a human readable message indicating details about the transition. This may be an empty... */ @@ -355,14 +354,14 @@ export type ProposalStatus = { status: 'True' | 'False' | 'Unknown'; /** type of condition in CamelCase or in foo.example.com/CamelCase. */ type: string; - })[]; + }[]; /** results references ExecutionResult CRs, newest last. Each entry corresponds to one execution atte... */ - results?: ({ + results?: { /** name is the name of the result CR. */ name: string; /** outcome indicates the result of this step attempt. Must be one of: Succeeded, Failed. */ outcome: 'Succeeded' | 'Failed'; - })[]; + }[]; /** retryCount tracks how many times execution+verification has been retried for the current analysis... */ retryCount?: number; /** sandbox tracks the sandbox used. */ @@ -376,7 +375,7 @@ export type ProposalStatus = { /** verification is the observed state of the verification step. */ verification?: { /** conditions for this step. */ - conditions?: ({ + conditions?: { /** lastTransitionTime is the last time the condition transitioned from one status to another. This s... */ lastTransitionTime: string; /** message is a human readable message indicating details about the transition. This may be an empty... */ @@ -389,14 +388,14 @@ export type ProposalStatus = { status: 'True' | 'False' | 'Unknown'; /** type of condition in CamelCase or in foo.example.com/CamelCase. */ type: string; - })[]; + }[]; /** results references VerificationResult CRs, newest last. Each entry corresponds to one verificatio... */ - results?: ({ + results?: { /** name is the name of the result CR. */ name: string; /** outcome indicates the result of this step attempt. Must be one of: Succeeded, Failed. */ outcome: 'Succeeded' | 'Failed'; - })[]; + }[]; /** sandbox tracks the sandbox used. */ sandbox?: { /** claimName is the name of the SandboxClaim resource that owns the sandbox pod. Maximum 253 charact... */ diff --git a/src/models/generated/analysisresults.ts b/src/models/generated/analysisresults.ts index 9a2fe5d..c59ce3a 100644 --- a/src/models/generated/analysisresults.ts +++ b/src/models/generated/analysisresults.ts @@ -1,7 +1,6 @@ // Auto-generated from CRD — do not edit manually. // Regenerate with: make generate-types - export type AnalysisResultSpec = { /** proposalName is the name of the parent Proposal in the same namespace. */ proposalName: string; @@ -9,7 +8,7 @@ export type AnalysisResultSpec = { export type AnalysisResultStatus = { /** conditions track the lifecycle of this result. */ - conditions?: ({ + conditions?: { /** lastTransitionTime is the last time the condition transitioned from one status to another. This s... */ lastTransitionTime: string; /** message is a human readable message indicating details about the transition. This may be an empty... */ @@ -22,11 +21,11 @@ export type AnalysisResultStatus = { status: 'True' | 'False' | 'Unknown'; /** type of condition in CamelCase or in foo.example.com/CamelCase. */ type: string; - })[]; + }[]; /** failureReason is populated when the step failed due to a system error. */ failureReason?: string; /** options contains the remediation options returned by the analysis agent. */ - options?: ({ + options?: { /** components contains optional adapter-defined structured data whose shape is determined by spec.an... */ components?: Record; /** diagnosis contains the root cause analysis specific to this option. Present when analysisOutput m... */ @@ -41,12 +40,12 @@ export type AnalysisResultStatus = { /** proposal contains the remediation plan for this option. Present when analysisOutput mode is Defau... */ proposal?: { /** actions is the ordered list of discrete actions the agent proposes. Maximum 50 items. */ - actions: ({ + actions: { /** description is a Markdown-formatted explanation of what this action will do (e.g., "Increase memo... */ description: string; /** type is the action category (e.g., "patch", "scale", "restart", "create", "delete", "rollout"). F... */ type: string; - })[]; + }[]; /** description is a Markdown-formatted summary of the overall remediation approach. Maximum 8192 cha... */ description: string; /** estimatedImpact is a Markdown-formatted description of the expected impact of the remediation on ... */ @@ -66,35 +65,35 @@ export type AnalysisResultStatus = { /** rbac contains the RBAC permissions the execution agent will need. The operator's policy engine va... */ rbac?: { /** clusterScoped are rules that will be applied via ClusterRole + ClusterRoleBinding. Used when the ... */ - clusterScoped?: ({ + clusterScoped?: { /** apiGroups are the API groups for this rule (e.g., "", "apps", "batch"). The empty string "" repre... */ - apiGroups: (string)[]; + apiGroups: string[]; /** justification is a Markdown-formatted explanation of why this permission is needed for the remedi... */ justification: string; /** namespace is the target namespace for namespace-scoped rules. Must match one of the proposal's ta... */ namespace?: string; /** resourceNames restricts the rule to specific named resources. When empty, the rule applies to all... */ - resourceNames?: (string)[]; + resourceNames?: string[]; /** resources are the resource types (e.g., "pods", "deployments"). Maximum 20 items. */ - resources: (string)[]; + resources: string[]; /** verbs are the allowed operations (e.g., "get", "patch", "delete"). Maximum 10 items. */ - verbs: (string)[]; - })[]; + verbs: string[]; + }[]; /** namespaceScoped are rules that will be applied via Role + RoleBinding in the proposal's target na... */ - namespaceScoped?: ({ + namespaceScoped?: { /** apiGroups are the API groups for this rule (e.g., "", "apps", "batch"). The empty string "" repre... */ - apiGroups: (string)[]; + apiGroups: string[]; /** justification is a Markdown-formatted explanation of why this permission is needed for the remedi... */ justification: string; /** namespace is the target namespace for namespace-scoped rules. Must match one of the proposal's ta... */ namespace?: string; /** resourceNames restricts the rule to specific named resources. When empty, the rule applies to all... */ - resourceNames?: (string)[]; + resourceNames?: string[]; /** resources are the resource types (e.g., "pods", "deployments"). Maximum 20 items. */ - resources: (string)[]; + resources: string[]; /** verbs are the allowed operations (e.g., "get", "patch", "delete"). Maximum 10 items. */ - verbs: (string)[]; - })[]; + verbs: string[]; + }[]; }; /** summary is an optional Markdown-formatted one-line summary for collapsed views in the console UI.... */ summary?: string; @@ -105,7 +104,7 @@ export type AnalysisResultStatus = { /** description is a Markdown-formatted summary of the verification approach. Maximum 4096 characters. */ description: string; /** steps is the ordered list of verification checks to run. Maximum 20 items. */ - steps?: ({ + steps?: { /** command is the command or API call to run for this check (e.g., "oc get pod -n production -l app=... */ command?: string; /** expected is the expected output or condition (e.g., "Running", "ready=true"). Maximum 1024 charac... */ @@ -114,9 +113,9 @@ export type AnalysisResultStatus = { name: string; /** type categorizes the check (e.g., "command", "metric", "condition"). Must be 1-256 characters. */ type: string; - })[]; + }[]; }; - })[]; + }[]; /** sandbox tracks the sandbox pod used for this analysis. */ sandbox?: { /** claimName is the name of the SandboxClaim resource that owns the sandbox pod. Maximum 253 charact... */ diff --git a/src/models/generated/approvalpolicies.ts b/src/models/generated/approvalpolicies.ts index 6894f68..8017814 100644 --- a/src/models/generated/approvalpolicies.ts +++ b/src/models/generated/approvalpolicies.ts @@ -1,17 +1,16 @@ // Auto-generated from CRD — do not edit manually. // Regenerate with: make generate-types - export type ApprovalPolicySpec = { /** maxAttempts sets the maximum number of execution retry attempts allowed for proposals. When verif... */ maxAttempts?: number; /** maxConcurrentProposals sets the maximum number of proposals the operator reconciles concurrently.... */ maxConcurrentProposals?: number; /** stages configures the approval mode for each workflow step. Omitted steps default to Manual. */ - stages?: ({ + stages?: { /** approval controls whether this step auto-approves or requires explicit user approval on the Propo... */ approval: 'Automatic' | 'Manual'; /** name is the workflow step this policy applies to. Allowed values: Analysis, Execution, Verificati... */ name: 'Analysis' | 'Execution' | 'Verification' | 'Escalation'; - })[]; + }[]; }; diff --git a/src/models/generated/index.ts b/src/models/generated/index.ts index 7df99e9..5d4222f 100644 --- a/src/models/generated/index.ts +++ b/src/models/generated/index.ts @@ -1,8 +1,7 @@ // Auto-generated from CRD — do not edit manually. // Regenerate with: make generate-types - -export * from './proposals'; -export * from './proposalapprovals'; +export * from './agenticruns'; +export * from './agenticrunapprovals'; export * from './approvalpolicies'; export * from './analysisresults'; From dd287b736d51c085c27d576f05811cedcbfaa64d Mon Sep 17 00:00:00 2001 From: Jakub Hadvig Date: Wed, 15 Jul 2026 13:33:42 +0200 Subject: [PATCH 2/2] fix: rename remaining proposalName and maxConcurrentProposals fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align generated types and tests with the CRD rename from lightspeed-agentic-operator#285: - proposalName → agenticRunName (AnalysisResultSpec) - maxConcurrentProposals → maxConcurrentRuns (ApprovalPolicySpec) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/__tests__/agenticrun.test.ts | 6 +++--- src/models/generated/analysisresults.ts | 4 ++-- src/models/generated/approvalpolicies.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/__tests__/agenticrun.test.ts b/src/__tests__/agenticrun.test.ts index 6f04fd6..00944a6 100644 --- a/src/__tests__/agenticrun.test.ts +++ b/src/__tests__/agenticrun.test.ts @@ -62,7 +62,7 @@ describe('getAnalysisDataFromResult', () => { }); it('returns empty components when result has no options', () => { - const result = { spec: { proposalName: 'test' }, status: {} } as LightspeedAnalysisResult; + const result = { spec: { agenticRunName: 'test' }, status: {} } as LightspeedAnalysisResult; const data = getAnalysisDataFromResult(result); expect(data.components).toEqual([]); }); @@ -73,7 +73,7 @@ describe('getAnalysisDataFromResult', () => { { type: 'ota_finding', severity: 'info', check: 'test', detail: 'ok' }, ]; const result = { - spec: { proposalName: 'test' }, + spec: { agenticRunName: 'test' }, status: { options: [{ title: 'Option', components: { analysisData: components } }] }, } as unknown as LightspeedAnalysisResult; const data = getAnalysisDataFromResult(result); @@ -84,7 +84,7 @@ describe('getAnalysisDataFromResult', () => { it('extracts legacy flat object from analysisData', () => { const legacyData = { decision: 'recommend', summary: 'All good' }; const result = { - spec: { proposalName: 'test' }, + spec: { agenticRunName: 'test' }, status: { options: [{ title: 'Option', components: { analysisData: legacyData } }] }, } as unknown as LightspeedAnalysisResult; const data = getAnalysisDataFromResult(result); diff --git a/src/models/generated/analysisresults.ts b/src/models/generated/analysisresults.ts index c59ce3a..be12714 100644 --- a/src/models/generated/analysisresults.ts +++ b/src/models/generated/analysisresults.ts @@ -2,8 +2,8 @@ // Regenerate with: make generate-types export type AnalysisResultSpec = { - /** proposalName is the name of the parent Proposal in the same namespace. */ - proposalName: string; + /** agenticRunName is the name of the parent AgenticRun in the same namespace. */ + agenticRunName: string; }; export type AnalysisResultStatus = { diff --git a/src/models/generated/approvalpolicies.ts b/src/models/generated/approvalpolicies.ts index 8017814..2d49822 100644 --- a/src/models/generated/approvalpolicies.ts +++ b/src/models/generated/approvalpolicies.ts @@ -4,8 +4,8 @@ export type ApprovalPolicySpec = { /** maxAttempts sets the maximum number of execution retry attempts allowed for proposals. When verif... */ maxAttempts?: number; - /** maxConcurrentProposals sets the maximum number of proposals the operator reconciles concurrently.... */ - maxConcurrentProposals?: number; + /** maxConcurrentRuns sets the maximum number of agentic runs the operator reconciles concurrently.... */ + maxConcurrentRuns?: number; /** stages configures the approval mode for each workflow step. Omitted steps default to Manual. */ stages?: { /** approval controls whether this step auto-approves or requires explicit user approval on the Propo... */