@resolveio/server-lib 22.3.176 → 22.3.178
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai/assistant-core-heuristics.d.ts +11 -0
- package/ai/assistant-core-heuristics.js +356 -0
- package/ai/assistant-core-heuristics.js.map +1 -0
- package/ai/resolveio-platform-intelligence-memory-corpus.d.ts +3 -0
- package/ai/resolveio-platform-intelligence-memory-corpus.js +214 -0
- package/ai/resolveio-platform-intelligence-memory-corpus.js.map +1 -0
- package/ai/resolveio-platform-intelligence-memory.d.ts +20 -0
- package/ai/resolveio-platform-intelligence-memory.js +341 -0
- package/ai/resolveio-platform-intelligence-memory.js.map +1 -0
- package/{src/ai/resolveio-platform-intelligence-types.ts → ai/resolveio-platform-intelligence-types.d.ts} +15 -20
- package/ai/resolveio-platform-intelligence-types.js +4 -0
- package/ai/resolveio-platform-intelligence-types.js.map +1 -0
- package/ai/resolveio-platform-intelligence.d.ts +6 -0
- package/ai/resolveio-platform-intelligence.js +463 -0
- package/ai/resolveio-platform-intelligence.js.map +1 -0
- package/client-server-app.d.ts +1 -0
- package/client-server-app.js +68 -0
- package/client-server-app.js.map +1 -0
- package/collections/ai-run.collection.d.ts +3 -0
- package/collections/ai-run.collection.js +170 -0
- package/collections/ai-run.collection.js.map +1 -0
- package/collections/ai-terminal-conversation.collection.d.ts +2 -0
- package/collections/ai-terminal-conversation.collection.js +140 -0
- package/collections/ai-terminal-conversation.collection.js.map +1 -0
- package/collections/ai-terminal-issue-report.collection.d.ts +2 -0
- package/collections/ai-terminal-issue-report.collection.js +148 -0
- package/collections/ai-terminal-issue-report.collection.js.map +1 -0
- package/collections/ai-terminal-message.collection.d.ts +2 -0
- package/collections/ai-terminal-message.collection.js +121 -0
- package/collections/ai-terminal-message.collection.js.map +1 -0
- package/collections/app-setting.collection.d.ts +3 -0
- package/collections/app-setting.collection.js +103 -0
- package/collections/app-setting.collection.js.map +1 -0
- package/collections/app-status.collection.d.ts +3 -0
- package/collections/app-status.collection.js +57 -0
- package/collections/app-status.collection.js.map +1 -0
- package/collections/communication-metric.collection.d.ts +2 -0
- package/collections/communication-metric.collection.js +133 -0
- package/collections/communication-metric.collection.js.map +1 -0
- package/collections/counter.collection.d.ts +3 -0
- package/collections/counter.collection.js +56 -0
- package/collections/counter.collection.js.map +1 -0
- package/collections/cron-job-history.collection.d.ts +3 -0
- package/collections/cron-job-history.collection.js +137 -0
- package/collections/cron-job-history.collection.js.map +1 -0
- package/collections/cron-job.collection.d.ts +3 -0
- package/collections/cron-job.collection.js +92 -0
- package/collections/cron-job.collection.js.map +1 -0
- package/collections/customer-notification.collection.d.ts +3 -0
- package/collections/customer-notification.collection.js +130 -0
- package/collections/customer-notification.collection.js.map +1 -0
- package/collections/customer-portal-password.collection.d.ts +3 -0
- package/collections/customer-portal-password.collection.js +75 -0
- package/collections/customer-portal-password.collection.js.map +1 -0
- package/collections/email-history.collection.d.ts +3 -0
- package/collections/email-history.collection.js +134 -0
- package/collections/email-history.collection.js.map +1 -0
- package/collections/email-verified.collection.d.ts +3 -0
- package/collections/email-verified.collection.js +62 -0
- package/collections/email-verified.collection.js.map +1 -0
- package/collections/file.collection.d.ts +3 -0
- package/collections/file.collection.js +74 -0
- package/collections/file.collection.js.map +1 -0
- package/collections/flag-update.collection.d.ts +3 -0
- package/collections/flag-update.collection.js +57 -0
- package/collections/flag-update.collection.js.map +1 -0
- package/collections/flag.collection.d.ts +3 -0
- package/collections/flag.collection.js +57 -0
- package/collections/flag.collection.js.map +1 -0
- package/collections/log-method-latency.collection.d.ts +3 -0
- package/collections/log-method-latency.collection.js +77 -0
- package/collections/log-method-latency.collection.js.map +1 -0
- package/collections/log-subscription.collection.d.ts +3 -0
- package/collections/log-subscription.collection.js +80 -0
- package/collections/log-subscription.collection.js.map +1 -0
- package/collections/log.collection.d.ts +3 -0
- package/collections/log.collection.js +93 -0
- package/collections/log.collection.js.map +1 -0
- package/collections/logged-in-users.collection.d.ts +3 -0
- package/collections/logged-in-users.collection.js +67 -0
- package/collections/logged-in-users.collection.js.map +1 -0
- package/collections/monitor-cpu.collection.d.ts +3 -0
- package/collections/monitor-cpu.collection.js +65 -0
- package/collections/monitor-cpu.collection.js.map +1 -0
- package/collections/monitor-function.collection.d.ts +3 -0
- package/collections/monitor-function.collection.js +74 -0
- package/collections/monitor-function.collection.js.map +1 -0
- package/collections/monitor-memory.collection.d.ts +3 -0
- package/collections/monitor-memory.collection.js +77 -0
- package/collections/monitor-memory.collection.js.map +1 -0
- package/collections/monitor-mongo.collection.d.ts +3 -0
- package/collections/monitor-mongo.collection.js +71 -0
- package/collections/monitor-mongo.collection.js.map +1 -0
- package/collections/notification.collection.d.ts +3 -0
- package/collections/notification.collection.js +57 -0
- package/collections/notification.collection.js.map +1 -0
- package/collections/openai-usage-ledger.collection.d.ts +2 -0
- package/collections/openai-usage-ledger.collection.js +188 -0
- package/collections/openai-usage-ledger.collection.js.map +1 -0
- package/collections/report-builder-dashboard-builder.collection.d.ts +3 -0
- package/collections/report-builder-dashboard-builder.collection.js +109 -0
- package/collections/report-builder-dashboard-builder.collection.js.map +1 -0
- package/collections/report-builder-library.collection.d.ts +3 -0
- package/collections/report-builder-library.collection.js +87 -0
- package/collections/report-builder-library.collection.js.map +1 -0
- package/collections/report-builder-report.collection.d.ts +4 -0
- package/collections/report-builder-report.collection.js +184 -0
- package/collections/report-builder-report.collection.js.map +1 -0
- package/collections/user-group.collection.d.ts +4 -0
- package/collections/user-group.collection.js +89 -0
- package/collections/user-group.collection.js.map +1 -0
- package/collections/user-guide.collection.d.ts +3 -0
- package/collections/user-guide.collection.js +57 -0
- package/collections/user-guide.collection.js.map +1 -0
- package/collections/user.collection.d.ts +4 -0
- package/collections/user.collection.js +180 -0
- package/collections/user.collection.js.map +1 -0
- package/cron/cron.d.ts +14 -0
- package/cron/cron.js +216 -0
- package/cron/cron.js.map +1 -0
- package/fixtures/cron-jobs.d.ts +1 -0
- package/fixtures/cron-jobs.js +150 -0
- package/fixtures/cron-jobs.js.map +1 -0
- package/fixtures/init.d.ts +1 -0
- package/fixtures/init.js +91 -0
- package/fixtures/init.js.map +1 -0
- package/http/auth.d.ts +2 -0
- package/http/auth.js +951 -0
- package/http/auth.js.map +1 -0
- package/http/health.d.ts +1 -0
- package/http/health.js +11 -0
- package/http/health.js.map +1 -0
- package/http/home.d.ts +1 -0
- package/http/home.js +134 -0
- package/http/home.js.map +1 -0
- package/http/slow-query-publication.d.ts +2 -0
- package/http/slow-query-publication.js +99 -0
- package/http/slow-query-publication.js.map +1 -0
- package/index.d.ts +1 -0
- package/index.js +19 -0
- package/index.js.map +1 -0
- package/managers/ai-assistant-codex-manager.manager.d.ts +67 -0
- package/managers/ai-assistant-codex-manager.manager.js +1113 -0
- package/managers/ai-assistant-codex-manager.manager.js.map +1 -0
- package/managers/ai-run-evidence.manager.d.ts +36 -0
- package/managers/ai-run-evidence.manager.js +377 -0
- package/managers/ai-run-evidence.manager.js.map +1 -0
- package/managers/communication-metric.manager.d.ts +16 -0
- package/managers/communication-metric.manager.js +134 -0
- package/managers/communication-metric.manager.js.map +1 -0
- package/managers/cron.manager.d.ts +20 -0
- package/managers/cron.manager.js +534 -0
- package/managers/cron.manager.js.map +1 -0
- package/managers/customer-notification-content.manager.d.ts +55 -0
- package/managers/customer-notification-content.manager.js +158 -0
- package/managers/customer-notification-content.manager.js.map +1 -0
- package/managers/diagnostic-manager-bootstrap.d.ts +9 -0
- package/managers/diagnostic-manager-bootstrap.js +260 -0
- package/managers/diagnostic-manager-bootstrap.js.map +1 -0
- package/managers/error-auto-fix.manager.d.ts +149 -0
- package/managers/error-auto-fix.manager.js +3064 -0
- package/managers/error-auto-fix.manager.js.map +1 -0
- package/managers/local-log.manager.d.ts +18 -0
- package/managers/local-log.manager.js +88 -0
- package/managers/local-log.manager.js.map +1 -0
- package/managers/method.manager.d.ts +84 -0
- package/managers/method.manager.js +1964 -0
- package/managers/method.manager.js.map +1 -0
- package/managers/mongo.manager.d.ts +224 -0
- package/managers/mongo.manager.js +5000 -0
- package/managers/mongo.manager.js.map +1 -0
- package/managers/monitor.manager.d.ts +70 -0
- package/managers/monitor.manager.js +550 -0
- package/managers/monitor.manager.js.map +1 -0
- package/managers/openai-usage-ledger.manager.d.ts +30 -0
- package/managers/openai-usage-ledger.manager.js +142 -0
- package/managers/openai-usage-ledger.manager.js.map +1 -0
- package/managers/slow-query-verifier.manager.d.ts +144 -0
- package/managers/slow-query-verifier.manager.js +3857 -0
- package/managers/slow-query-verifier.manager.js.map +1 -0
- package/managers/slow-query.manager.d.ts +28 -0
- package/managers/slow-query.manager.js +468 -0
- package/managers/slow-query.manager.js.map +1 -0
- package/managers/subscription.manager.d.ts +169 -0
- package/managers/subscription.manager.js +3434 -0
- package/managers/subscription.manager.js.map +1 -0
- package/managers/websocket.manager.d.ts +73 -0
- package/managers/websocket.manager.js +673 -0
- package/managers/websocket.manager.js.map +1 -0
- package/managers/worker-dispatcher.manager.d.ts +120 -0
- package/managers/worker-dispatcher.manager.js +1266 -0
- package/managers/worker-dispatcher.manager.js.map +1 -0
- package/managers/worker-server.manager.d.ts +35 -0
- package/managers/worker-server.manager.js +582 -0
- package/managers/worker-server.manager.js.map +1 -0
- package/methods/accounts.d.ts +2 -0
- package/methods/accounts.js +624 -0
- package/methods/accounts.js.map +1 -0
- package/methods/ai-terminal.d.ts +338 -0
- package/methods/ai-terminal.js +23454 -0
- package/methods/ai-terminal.js.map +1 -0
- package/methods/app-settings.d.ts +2 -0
- package/methods/app-settings.js +169 -0
- package/methods/app-settings.js.map +1 -0
- package/methods/aws.d.ts +2 -0
- package/methods/aws.js +877 -0
- package/methods/aws.js.map +1 -0
- package/methods/collections.d.ts +2 -0
- package/methods/collections.js +719 -0
- package/methods/collections.js.map +1 -0
- package/methods/counters.d.ts +2 -0
- package/methods/counters.js +113 -0
- package/methods/counters.js.map +1 -0
- package/methods/cron-jobs.d.ts +2 -0
- package/methods/cron-jobs.js +2475 -0
- package/methods/cron-jobs.js.map +1 -0
- package/methods/customer-notifications.d.ts +2 -0
- package/methods/customer-notifications.js +528 -0
- package/methods/customer-notifications.js.map +1 -0
- package/methods/diagnostics.d.ts +2 -0
- package/methods/diagnostics.js +703 -0
- package/methods/diagnostics.js.map +1 -0
- package/methods/flag-updates.d.ts +2 -0
- package/methods/flag-updates.js +8 -0
- package/methods/flag-updates.js.map +1 -0
- package/methods/flags.d.ts +2 -0
- package/methods/flags.js +8 -0
- package/methods/flags.js.map +1 -0
- package/methods/logs.d.ts +2 -0
- package/methods/logs.js +751 -0
- package/methods/logs.js.map +1 -0
- package/methods/mongo-explorer.d.ts +2 -0
- package/methods/mongo-explorer.js +1808 -0
- package/methods/mongo-explorer.js.map +1 -0
- package/methods/monitor.d.ts +2 -0
- package/methods/monitor.js +543 -0
- package/methods/monitor.js.map +1 -0
- package/methods/pdf.d.ts +2 -0
- package/methods/pdf.js +1216 -0
- package/methods/pdf.js.map +1 -0
- package/methods/publications.d.ts +1 -0
- package/methods/publications.js +183 -0
- package/methods/publications.js.map +1 -0
- package/methods/report-builder.d.ts +2 -0
- package/methods/report-builder.js +3094 -0
- package/methods/report-builder.js.map +1 -0
- package/methods/support.d.ts +2 -0
- package/methods/support.js +430 -0
- package/methods/support.js.map +1 -0
- package/models/ai-run.model.d.ts +19 -0
- package/models/ai-run.model.js +4 -0
- package/models/ai-run.model.js.map +1 -0
- package/models/ai-terminal-conversation.model.d.ts +17 -0
- package/models/ai-terminal-conversation.model.js +4 -0
- package/models/ai-terminal-conversation.model.js.map +1 -0
- package/models/ai-terminal-issue-report.model.d.ts +19 -0
- package/models/ai-terminal-issue-report.model.js +4 -0
- package/models/ai-terminal-issue-report.model.js.map +1 -0
- package/models/ai-terminal-message.model.d.ts +22 -0
- package/models/ai-terminal-message.model.js +4 -0
- package/models/ai-terminal-message.model.js.map +1 -0
- package/models/app-setting.model.d.ts +16 -0
- package/models/app-setting.model.js +4 -0
- package/models/app-setting.model.js.map +1 -0
- package/{src/models/app-status.model.ts → models/app-status.model.d.ts} +2 -3
- package/models/app-status.model.js +4 -0
- package/models/app-status.model.js.map +1 -0
- package/{src/models/billing-logged-in-users.model.ts → models/billing-logged-in-users.model.d.ts} +4 -5
- package/models/billing-logged-in-users.model.js +4 -0
- package/models/billing-logged-in-users.model.js.map +1 -0
- package/models/collection-document.model.d.ts +21 -0
- package/models/collection-document.model.js +4 -0
- package/models/collection-document.model.js.map +1 -0
- package/models/communication-metric.model.d.ts +20 -0
- package/models/communication-metric.model.js +4 -0
- package/models/communication-metric.model.js.map +1 -0
- package/{src/models/counter.model.ts → models/counter.model.d.ts} +3 -4
- package/models/counter.model.js +4 -0
- package/models/counter.model.js.map +1 -0
- package/models/cron-job-history.model.d.ts +15 -0
- package/models/cron-job-history.model.js +4 -0
- package/models/cron-job-history.model.js.map +1 -0
- package/models/cron-job.model.d.ts +14 -0
- package/models/cron-job.model.js +4 -0
- package/models/cron-job.model.js.map +1 -0
- package/models/customer-notification.model.d.ts +26 -0
- package/models/customer-notification.model.js +4 -0
- package/models/customer-notification.model.js.map +1 -0
- package/models/customer-portal-password.model.d.ts +11 -0
- package/models/customer-portal-password.model.js +4 -0
- package/models/customer-portal-password.model.js.map +1 -0
- package/models/dialog.model.d.ts +23 -0
- package/models/dialog.model.js +4 -0
- package/models/dialog.model.js.map +1 -0
- package/models/email-history.model.d.ts +32 -0
- package/{src/models/email-history.model.ts → models/email-history.model.js} +4 -36
- package/models/email-history.model.js.map +1 -0
- package/{src/models/email-verified.model.ts → models/email-verified.model.d.ts} +5 -6
- package/models/email-verified.model.js +4 -0
- package/models/email-verified.model.js.map +1 -0
- package/{src/models/file.model.ts → models/file.model.d.ts} +7 -8
- package/models/file.model.js +4 -0
- package/models/file.model.js.map +1 -0
- package/{src/models/flag-update.model.ts → models/flag-update.model.d.ts} +3 -4
- package/models/flag-update.model.js +4 -0
- package/models/flag-update.model.js.map +1 -0
- package/{src/models/flag.model.ts → models/flag.model.d.ts} +3 -4
- package/models/flag.model.js +4 -0
- package/models/flag.model.js.map +1 -0
- package/models/log-method-latency.model.d.ts +10 -0
- package/models/log-method-latency.model.js +4 -0
- package/models/log-method-latency.model.js.map +1 -0
- package/{src/models/log-subscription.model.ts → models/log-subscription.model.d.ts} +9 -11
- package/models/log-subscription.model.js +4 -0
- package/models/log-subscription.model.js.map +1 -0
- package/models/log.model.d.ts +17 -0
- package/models/log.model.js +4 -0
- package/models/log.model.js.map +1 -0
- package/{src/models/logged-in-users.model.ts → models/logged-in-users.model.d.ts} +5 -6
- package/models/logged-in-users.model.js +4 -0
- package/models/logged-in-users.model.js.map +1 -0
- package/{src/models/method-response.model.ts → models/method-response.model.d.ts} +6 -7
- package/models/method-response.model.js +4 -0
- package/models/method-response.model.js.map +1 -0
- package/models/method.model.d.ts +26 -0
- package/models/method.model.js +4 -0
- package/models/method.model.js.map +1 -0
- package/{src/models/monitor-cpu.model.ts → models/monitor-cpu.model.d.ts} +7 -9
- package/models/monitor-cpu.model.js +4 -0
- package/models/monitor-cpu.model.js.map +1 -0
- package/models/monitor-function.model.d.ts +14 -0
- package/models/monitor-function.model.js +4 -0
- package/models/monitor-function.model.js.map +1 -0
- package/models/monitor-memory.model.d.ts +15 -0
- package/models/monitor-memory.model.js +4 -0
- package/models/monitor-memory.model.js.map +1 -0
- package/models/monitor-mongo.model.d.ts +13 -0
- package/models/monitor-mongo.model.js +4 -0
- package/models/monitor-mongo.model.js.map +1 -0
- package/{src/models/notification.model.ts → models/notification.model.d.ts} +4 -6
- package/models/notification.model.js +4 -0
- package/models/notification.model.js.map +1 -0
- package/models/openai-usage-ledger.model.d.ts +30 -0
- package/models/openai-usage-ledger.model.js +4 -0
- package/models/openai-usage-ledger.model.js.map +1 -0
- package/models/pagination.model.d.ts +11 -0
- package/models/pagination.model.js +28 -0
- package/models/pagination.model.js.map +1 -0
- package/models/permission.model.d.ts +12 -0
- package/models/permission.model.js +4 -0
- package/models/permission.model.js.map +1 -0
- package/models/report-builder-dashboard-builder.model.d.ts +25 -0
- package/models/report-builder-dashboard-builder.model.js +4 -0
- package/models/report-builder-dashboard-builder.model.js.map +1 -0
- package/models/report-builder-library.model.d.ts +17 -0
- package/models/report-builder-library.model.js +4 -0
- package/models/report-builder-library.model.js.map +1 -0
- package/models/report-builder-report.model.d.ts +121 -0
- package/models/report-builder-report.model.js +4 -0
- package/models/report-builder-report.model.js.map +1 -0
- package/models/report-builder.model.d.ts +61 -0
- package/models/report-builder.model.js +4 -0
- package/models/report-builder.model.js.map +1 -0
- package/models/select-data-label.model.d.ts +9 -0
- package/models/select-data-label.model.js +4 -0
- package/models/select-data-label.model.js.map +1 -0
- package/models/server-message.model.d.ts +32 -0
- package/models/server-message.model.js +4 -0
- package/models/server-message.model.js.map +1 -0
- package/models/slow-query-report.model.d.ts +23 -0
- package/models/slow-query-report.model.js +4 -0
- package/models/slow-query-report.model.js.map +1 -0
- package/models/subscription.model.d.ts +31 -0
- package/models/subscription.model.js +4 -0
- package/models/subscription.model.js.map +1 -0
- package/models/support-ticket.model.d.ts +87 -0
- package/models/support-ticket.model.js +4 -0
- package/models/support-ticket.model.js.map +1 -0
- package/models/user-group.model.d.ts +20 -0
- package/models/user-group.model.js +4 -0
- package/models/user-group.model.js.map +1 -0
- package/{src/models/user-guide.model.ts → models/user-guide.model.d.ts} +4 -5
- package/models/user-guide.model.js +4 -0
- package/models/user-guide.model.js.map +1 -0
- package/models/user.model.d.ts +84 -0
- package/models/user.model.js +4 -0
- package/models/user.model.js.map +1 -0
- package/package.json +1 -1
- package/private/images/ResolveIO.png +0 -0
- package/public_api.js +127 -0
- package/public_api.js.map +1 -0
- package/publications/ai-terminal.d.ts +1 -0
- package/publications/ai-terminal.js +122 -0
- package/publications/ai-terminal.js.map +1 -0
- package/publications/app-settings.d.ts +2 -0
- package/publications/app-settings.js +28 -0
- package/publications/app-settings.js.map +1 -0
- package/publications/app-status.d.ts +2 -0
- package/publications/app-status.js +16 -0
- package/publications/app-status.js.map +1 -0
- package/publications/cron-jobs.d.ts +2 -0
- package/publications/cron-jobs.js +88 -0
- package/publications/cron-jobs.js.map +1 -0
- package/publications/customer-notifications.d.ts +2 -0
- package/publications/customer-notifications.js +161 -0
- package/publications/customer-notifications.js.map +1 -0
- package/publications/files.d.ts +2 -0
- package/publications/files.js +36 -0
- package/publications/files.js.map +1 -0
- package/publications/flags-update.d.ts +2 -0
- package/publications/flags-update.js +22 -0
- package/publications/flags-update.js.map +1 -0
- package/publications/flags.d.ts +2 -0
- package/publications/flags.js +22 -0
- package/publications/flags.js.map +1 -0
- package/publications/logs.d.ts +2 -0
- package/publications/logs.js +164 -0
- package/publications/logs.js.map +1 -0
- package/publications/notifications.d.ts +2 -0
- package/publications/notifications.js +16 -0
- package/publications/notifications.js.map +1 -0
- package/publications/report-builder-dashboard-builders.d.ts +2 -0
- package/publications/report-builder-dashboard-builders.js +42 -0
- package/publications/report-builder-dashboard-builders.js.map +1 -0
- package/publications/report-builder-libraries.d.ts +2 -0
- package/publications/report-builder-libraries.js +90 -0
- package/publications/report-builder-libraries.js.map +1 -0
- package/publications/report-builder-reports.d.ts +2 -0
- package/publications/report-builder-reports.js +50 -0
- package/publications/report-builder-reports.js.map +1 -0
- package/publications/super-admin.d.ts +2 -0
- package/publications/super-admin.js +16 -0
- package/publications/super-admin.js.map +1 -0
- package/publications/user-groups.d.ts +1 -0
- package/publications/user-groups.js +16 -0
- package/publications/user-groups.js.map +1 -0
- package/publications/user-guides.d.ts +1 -0
- package/publications/user-guides.js +16 -0
- package/publications/user-guides.js.map +1 -0
- package/resolveio-server-app.d.ts +70 -0
- package/resolveio-server-app.js +801 -0
- package/resolveio-server-app.js.map +1 -0
- package/server-app.d.ts +228 -0
- package/server-app.js +3566 -0
- package/server-app.js.map +1 -0
- package/services/codex-client.d.ts +128 -0
- package/services/codex-client.js +1629 -0
- package/services/codex-client.js.map +1 -0
- package/services/openai-client.d.ts +46 -0
- package/services/openai-client.js +318 -0
- package/services/openai-client.js.map +1 -0
- package/types/error-report.d.ts +25 -0
- package/types/error-report.js +4 -0
- package/types/error-report.js.map +1 -0
- package/types/slow-query-report.d.ts +27 -0
- package/types/slow-query-report.js +6 -0
- package/types/slow-query-report.js.map +1 -0
- package/util/ai-qa-policy.d.ts +124 -0
- package/util/ai-qa-policy.js +736 -0
- package/util/ai-qa-policy.js.map +1 -0
- package/util/ai-run-evidence-adapters.d.ts +73 -0
- package/util/ai-run-evidence-adapters.js +3087 -0
- package/util/ai-run-evidence-adapters.js.map +1 -0
- package/util/ai-run-evidence-dashboard.d.ts +84 -0
- package/util/ai-run-evidence-dashboard.js +336 -0
- package/util/ai-run-evidence-dashboard.js.map +1 -0
- package/util/ai-run-evidence-eval.d.ts +86 -0
- package/util/ai-run-evidence-eval.js +1015 -0
- package/util/ai-run-evidence-eval.js.map +1 -0
- package/util/ai-run-evidence.d.ts +244 -0
- package/util/ai-run-evidence.js +792 -0
- package/util/ai-run-evidence.js.map +1 -0
- package/util/ai-runner-artifacts.d.ts +82 -0
- package/util/ai-runner-artifacts.js +713 -0
- package/util/ai-runner-artifacts.js.map +1 -0
- package/util/ai-runner-manager-autopilot.d.ts +210 -0
- package/util/ai-runner-manager-autopilot.js +642 -0
- package/util/ai-runner-manager-autopilot.js.map +1 -0
- package/util/ai-runner-manager-policy.d.ts +653 -0
- package/util/ai-runner-manager-policy.js +2880 -0
- package/util/ai-runner-manager-policy.js.map +1 -0
- package/util/ai-runner-qa-auth.d.ts +5 -0
- package/util/ai-runner-qa-auth.js +822 -0
- package/util/ai-runner-qa-auth.js.map +1 -0
- package/util/ai-runner-qa-tools.d.ts +26 -0
- package/util/ai-runner-qa-tools.js +3029 -0
- package/util/ai-runner-qa-tools.js.map +1 -0
- package/util/aicoder-runner-v6.d.ts +367 -0
- package/util/aicoder-runner-v6.js +1925 -0
- package/util/aicoder-runner-v6.js.map +1 -0
- package/util/common.d.ts +31 -0
- package/util/common.js +683 -0
- package/util/common.js.map +1 -0
- package/util/customer-portal-password.d.ts +13 -0
- package/util/customer-portal-password.js +209 -0
- package/util/customer-portal-password.js.map +1 -0
- package/util/error-reporter.d.ts +52 -0
- package/util/error-reporter.js +326 -0
- package/util/error-reporter.js.map +1 -0
- package/util/error-tracking.d.ts +13 -0
- package/util/error-tracking.js +120 -0
- package/util/error-tracking.js.map +1 -0
- package/util/openai-usage-cost.d.ts +6 -0
- package/util/openai-usage-cost.js +103 -0
- package/util/openai-usage-cost.js.map +1 -0
- package/util/report-builder-unwinds.d.ts +15 -0
- package/util/report-builder-unwinds.js +156 -0
- package/util/report-builder-unwinds.js.map +1 -0
- package/util/runner-process-janitor.d.ts +27 -0
- package/util/runner-process-janitor.js +208 -0
- package/util/runner-process-janitor.js.map +1 -0
- package/util/schema-report-builder.d.ts +6 -0
- package/util/schema-report-builder.js +481 -0
- package/util/schema-report-builder.js.map +1 -0
- package/util/slow-query-reporter.d.ts +28 -0
- package/util/slow-query-reporter.js +226 -0
- package/util/slow-query-reporter.js.map +1 -0
- package/util/subscription-dependency-context.d.ts +34 -0
- package/util/subscription-dependency-context.js +1283 -0
- package/util/subscription-dependency-context.js.map +1 -0
- package/util/support-runner-v5.d.ts +841 -0
- package/util/support-runner-v5.js +3723 -0
- package/util/support-runner-v5.js.map +1 -0
- package/util/tokenizer.d.ts +5 -0
- package/util/tokenizer.js +41 -0
- package/util/tokenizer.js.map +1 -0
- package/workers/codex-runner.worker.d.ts +1 -0
- package/workers/codex-runner.worker.js +192 -0
- package/workers/codex-runner.worker.js.map +1 -0
- package/.nodemon.json +0 -5
- package/.vscode/settings.json +0 -21
- package/AGENTS.md +0 -195
- package/README.md +0 -22
- package/build_package.sh +0 -5
- package/compileDTS.pl +0 -64
- package/docs/ai-assistant-nightly-eval.md +0 -65
- package/docs/ai-assistant-preflight-checklist.md +0 -23
- package/docs/ai-assistant-report-builder-bridge-playbook.md +0 -115
- package/eslint-plugin-custom/index.js +0 -7
- package/eslint-plugin-custom/rules/no-filter-zero-index.js +0 -44
- package/eslint.config.js +0 -103
- package/gulpfile.js +0 -216
- package/methodAndPublicationListGenerator.py +0 -375
- package/mongodbensurers.js +0 -2
- package/mongostop.js +0 -3
- package/scripts/cleanup-bypassed-callmethod-logs.js +0 -616
- package/settings.development.json +0 -25
- package/settings.development.redacted.json +0 -25
- package/src/.env +0 -12
- package/src/ai/assistant-core-heuristics.ts +0 -379
- package/src/ai/resolveio-platform-intelligence-memory-corpus.ts +0 -185
- package/src/ai/resolveio-platform-intelligence-memory.ts +0 -325
- package/src/ai/resolveio-platform-intelligence.ts +0 -462
- package/src/client-server-app.ts +0 -12
- package/src/collections/ai-run.collection.ts +0 -117
- package/src/collections/ai-terminal-conversation.collection.ts +0 -91
- package/src/collections/ai-terminal-issue-report.collection.ts +0 -99
- package/src/collections/ai-terminal-message.collection.ts +0 -77
- package/src/collections/app-setting.collection.ts +0 -104
- package/src/collections/app-status.collection.ts +0 -58
- package/src/collections/communication-metric.collection.ts +0 -84
- package/src/collections/counter.collection.ts +0 -56
- package/src/collections/cron-job-history.collection.ts +0 -94
- package/src/collections/cron-job.collection.ts +0 -92
- package/src/collections/customer-notification.collection.ts +0 -131
- package/src/collections/customer-portal-password.collection.ts +0 -76
- package/src/collections/email-history.collection.ts +0 -134
- package/src/collections/email-verified.collection.ts +0 -62
- package/src/collections/file.collection.ts +0 -74
- package/src/collections/flag-update.collection.ts +0 -57
- package/src/collections/flag.collection.ts +0 -57
- package/src/collections/log-method-latency.collection.ts +0 -77
- package/src/collections/log-subscription.collection.ts +0 -80
- package/src/collections/log.collection.ts +0 -93
- package/src/collections/logged-in-users.collection.ts +0 -67
- package/src/collections/monitor-cpu.collection.ts +0 -65
- package/src/collections/monitor-function.collection.ts +0 -74
- package/src/collections/monitor-memory.collection.ts +0 -77
- package/src/collections/monitor-mongo.collection.ts +0 -71
- package/src/collections/notification.collection.ts +0 -57
- package/src/collections/openai-usage-ledger.collection.ts +0 -131
- package/src/collections/report-builder-dashboard-builder.collection.ts +0 -109
- package/src/collections/report-builder-library.collection.ts +0 -89
- package/src/collections/report-builder-report.collection.ts +0 -184
- package/src/collections/user-group.collection.ts +0 -89
- package/src/collections/user-guide.collection.ts +0 -57
- package/src/collections/user.collection.ts +0 -181
- package/src/cron/cron.ts +0 -117
- package/src/fixtures/cron-jobs.ts +0 -95
- package/src/fixtures/init.ts +0 -35
- package/src/http/auth.ts +0 -818
- package/src/http/health.ts +0 -7
- package/src/http/home.ts +0 -90
- package/src/http/slow-query-publication.ts +0 -49
- package/src/index.ts +0 -1
- package/src/managers/ai-assistant-codex-manager.manager.ts +0 -1131
- package/src/managers/ai-run-evidence.manager.ts +0 -264
- package/src/managers/communication-metric.manager.ts +0 -82
- package/src/managers/cron.manager.ts +0 -333
- package/src/managers/customer-notification-content.manager.ts +0 -236
- package/src/managers/diagnostic-manager-bootstrap.ts +0 -165
- package/src/managers/error-auto-fix.manager.ts +0 -2767
- package/src/managers/local-log.manager.ts +0 -113
- package/src/managers/method.manager.ts +0 -1857
- package/src/managers/mongo.manager.ts +0 -4575
- package/src/managers/monitor.manager.ts +0 -507
- package/src/managers/openai-usage-ledger.manager.ts +0 -112
- package/src/managers/slow-query-verifier.manager.ts +0 -3590
- package/src/managers/slow-query.manager.ts +0 -519
- package/src/managers/subscription.manager.ts +0 -3128
- package/src/managers/websocket.manager.ts +0 -746
- package/src/managers/worker-dispatcher.manager.ts +0 -1360
- package/src/managers/worker-server.manager.ts +0 -536
- package/src/methods/accounts.ts +0 -532
- package/src/methods/ai-terminal.ts +0 -23825
- package/src/methods/app-settings.ts +0 -114
- package/src/methods/aws.ts +0 -649
- package/src/methods/collections.ts +0 -641
- package/src/methods/counters.ts +0 -69
- package/src/methods/cron-jobs.ts +0 -2614
- package/src/methods/customer-notifications.ts +0 -458
- package/src/methods/diagnostics.ts +0 -616
- package/src/methods/flag-updates.ts +0 -7
- package/src/methods/flags.ts +0 -7
- package/src/methods/logs.ts +0 -657
- package/src/methods/mongo-explorer.ts +0 -1880
- package/src/methods/monitor.ts +0 -540
- package/src/methods/pdf.ts +0 -1236
- package/src/methods/publications.ts +0 -129
- package/src/methods/report-builder.ts +0 -3300
- package/src/methods/support.ts +0 -335
- package/src/models/ai-run.model.ts +0 -27
- package/src/models/ai-terminal-conversation.model.ts +0 -19
- package/src/models/ai-terminal-issue-report.model.ts +0 -21
- package/src/models/ai-terminal-message.model.ts +0 -24
- package/src/models/app-setting.model.ts +0 -17
- package/src/models/collection-document.model.ts +0 -24
- package/src/models/communication-metric.model.ts +0 -23
- package/src/models/cron-job-history.model.ts +0 -16
- package/src/models/cron-job.model.ts +0 -15
- package/src/models/customer-notification.model.ts +0 -28
- package/src/models/customer-portal-password.model.ts +0 -12
- package/src/models/dialog.model.ts +0 -25
- package/src/models/log-method-latency.model.ts +0 -11
- package/src/models/log.model.ts +0 -19
- package/src/models/method.model.ts +0 -25
- package/src/models/monitor-function.model.ts +0 -16
- package/src/models/monitor-memory.model.ts +0 -17
- package/src/models/monitor-mongo.model.ts +0 -15
- package/src/models/openai-usage-ledger.model.ts +0 -56
- package/src/models/pagination.model.ts +0 -35
- package/src/models/permission.model.ts +0 -14
- package/src/models/report-builder-dashboard-builder.model.ts +0 -29
- package/src/models/report-builder-library.model.ts +0 -20
- package/src/models/report-builder-report.model.ts +0 -136
- package/src/models/report-builder.model.ts +0 -68
- package/src/models/select-data-label.model.ts +0 -9
- package/src/models/server-message.model.ts +0 -31
- package/src/models/slow-query-report.model.ts +0 -23
- package/src/models/subscription.model.ts +0 -73
- package/src/models/support-ticket.model.ts +0 -104
- package/src/models/user-group.model.ts +0 -24
- package/src/models/user.model.ts +0 -96
- package/src/private/images/ResolveIO.png +0 -0
- package/src/publications/ai-terminal.ts +0 -73
- package/src/publications/app-settings.ts +0 -25
- package/src/publications/app-status.ts +0 -13
- package/src/publications/cron-jobs.ts +0 -40
- package/src/publications/customer-notifications.ts +0 -101
- package/src/publications/files.ts +0 -33
- package/src/publications/flags-update.ts +0 -19
- package/src/publications/flags.ts +0 -19
- package/src/publications/logs.ts +0 -163
- package/src/publications/notifications.ts +0 -13
- package/src/publications/report-builder-dashboard-builders.ts +0 -39
- package/src/publications/report-builder-libraries.ts +0 -41
- package/src/publications/report-builder-reports.ts +0 -47
- package/src/publications/super-admin.ts +0 -13
- package/src/publications/user-groups.ts +0 -12
- package/src/publications/user-guides.ts +0 -12
- package/src/resolveio-server-app.ts +0 -617
- package/src/server-app.ts +0 -3354
- package/src/services/codex-client.ts +0 -1231
- package/src/services/openai-client.ts +0 -265
- package/src/types/error-report.ts +0 -26
- package/src/types/js-tiktoken.d.ts +0 -11
- package/src/types/slow-query-report.ts +0 -28
- package/src/util/ai-qa-policy.ts +0 -925
- package/src/util/ai-run-evidence-adapters.ts +0 -3292
- package/src/util/ai-run-evidence-dashboard.ts +0 -313
- package/src/util/ai-run-evidence-eval.ts +0 -1054
- package/src/util/ai-run-evidence.ts +0 -1124
- package/src/util/ai-runner-artifacts.ts +0 -586
- package/src/util/ai-runner-manager-autopilot.ts +0 -961
- package/src/util/ai-runner-manager-policy.ts +0 -4193
- package/src/util/ai-runner-qa-auth.ts +0 -821
- package/src/util/ai-runner-qa-tools.ts +0 -3045
- package/src/util/aicoder-runner-v6.ts +0 -2510
- package/src/util/common.ts +0 -649
- package/src/util/customer-portal-password.ts +0 -183
- package/src/util/error-reporter.ts +0 -332
- package/src/util/error-tracking.ts +0 -79
- package/src/util/openai-usage-cost.ts +0 -114
- package/src/util/report-builder-unwinds.ts +0 -180
- package/src/util/runner-process-janitor.ts +0 -219
- package/src/util/schema-report-builder.ts +0 -448
- package/src/util/slow-query-reporter.ts +0 -216
- package/src/util/subscription-dependency-context.ts +0 -1096
- package/src/util/support-runner-v5.ts +0 -4785
- package/src/util/tokenizer.ts +0 -38
- package/src/workers/codex-runner.worker.ts +0 -142
- package/start_server.sh +0 -5
- package/tests/ai-assistant-corpus-build.ts +0 -484
- package/tests/ai-assistant-corpus-replay-e2e.ts +0 -774
- package/tests/ai-assistant-data-parity-e2e.ts +0 -1989
- package/tests/ai-assistant-eval-triage.ts +0 -831
- package/tests/ai-assistant-openai-e2e.ts +0 -1061
- package/tests/ai-assistant-openai-git-e2e.ts +0 -155
- package/tests/ai-assistant-preflight-matrix.ts +0 -215
- package/tests/ai-assistant-routing-eval.test.ts +0 -560
- package/tests/ai-assistant-snf-live-eval.ts +0 -975
- package/tests/ai-assistant-utils.test.ts +0 -3057
- package/tests/ai-manager-autopilot-snapshot.test.ts +0 -193
- package/tests/ai-manager-recovery-checkpoint.test.ts +0 -1136
- package/tests/ai-run-eval.test.ts +0 -112
- package/tests/ai-run-evidence.test.ts +0 -1403
- package/tests/ai-runner-contract.test.ts +0 -488
- package/tests/aicoder-runner-v6.test.ts +0 -676
- package/tests/error-reporter.test.ts +0 -145
- package/tests/method-publication-generator.test.ts +0 -46
- package/tests/report-builder-linking.test.ts +0 -79
- package/tests/resolveio-platform-intelligence.test.ts +0 -352
- package/tests/server-app-cron-owner.test.ts +0 -127
- package/tests/subscription-connect-race.test.ts +0 -158
- package/tests/subscription-dependency-context.test.ts +0 -324
- package/tests/subscription-manager-collection-tracking.test.ts +0 -86
- package/tests/subscription-manager-invalidation.test.ts +0 -86
- package/tests/support-runner-v5.test.ts +0 -1166
- package/tsconfig.json +0 -34
- /package/{src/private → private}/email-templates/enrollment.html +0 -0
- /package/{src/private → private}/email-templates/forgot-password.html +0 -0
- /package/{src/private → private}/email-templates/support-ticket-deleted.html +0 -0
- /package/{src/private → private}/email-templates/support-ticket-modified.html +0 -0
- /package/{src/private → private}/email-templates/support-ticket.html +0 -0
- /package/{src/public_api.ts → public_api.d.ts} +0 -0
|
@@ -1,4193 +0,0 @@
|
|
|
1
|
-
export type ResolveIOAIManagerAction =
|
|
2
|
-
| 'continue'
|
|
3
|
-
| 'retry_infra'
|
|
4
|
-
| 'reset_loop_budget'
|
|
5
|
-
| 'park_repeated_failure'
|
|
6
|
-
| 'park_ping_pong'
|
|
7
|
-
| 'manual_handoff';
|
|
8
|
-
|
|
9
|
-
export interface ResolveIOAIManagerFailureRecord {
|
|
10
|
-
outcome?: string;
|
|
11
|
-
lane?: string;
|
|
12
|
-
stepType?: string;
|
|
13
|
-
failureClass?: string;
|
|
14
|
-
blocker?: string;
|
|
15
|
-
blockerFingerprint?: string;
|
|
16
|
-
evidenceHash?: string;
|
|
17
|
-
changedFiles?: string[];
|
|
18
|
-
artifactPaths?: string[];
|
|
19
|
-
summary?: string;
|
|
20
|
-
recordedAt?: Date | string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type ResolveIOAIManagerEvidenceStrength = 'none' | 'weak' | 'material' | 'proof';
|
|
24
|
-
|
|
25
|
-
export interface ResolveIOAIManagerEvidenceAssessment {
|
|
26
|
-
changed: boolean;
|
|
27
|
-
material: boolean;
|
|
28
|
-
strength: ResolveIOAIManagerEvidenceStrength;
|
|
29
|
-
signals: string[];
|
|
30
|
-
evidenceHash: string;
|
|
31
|
-
blockerFingerprint: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export type ResolveIOAIManagerRecoveryClass =
|
|
35
|
-
| 'advance_after_proof'
|
|
36
|
-
| 'diagnosis_only'
|
|
37
|
-
| 'diagnosis_revision'
|
|
38
|
-
| 'infra_repair'
|
|
39
|
-
| 'compile_repair'
|
|
40
|
-
| 'journey_contract_repair'
|
|
41
|
-
| 'business_proof_repair'
|
|
42
|
-
| 'release_repair'
|
|
43
|
-
| 'product_code_repair'
|
|
44
|
-
| 'blocked_until_new_evidence'
|
|
45
|
-
| 'manual_handoff'
|
|
46
|
-
| 'continue';
|
|
47
|
-
|
|
48
|
-
export interface ResolveIOAIManagerRecoveryPlan {
|
|
49
|
-
recoveryClass: ResolveIOAIManagerRecoveryClass;
|
|
50
|
-
lane: string;
|
|
51
|
-
stepType: string;
|
|
52
|
-
nextActionLabel: string;
|
|
53
|
-
objective: string;
|
|
54
|
-
allowedAction: string;
|
|
55
|
-
productRepairAllowed: boolean;
|
|
56
|
-
expensiveModelAllowed: boolean;
|
|
57
|
-
maxAttemptsBeforePark: number;
|
|
58
|
-
finiteSteps: string[];
|
|
59
|
-
requiredEvidence: string[];
|
|
60
|
-
loopResetEvidence: string[];
|
|
61
|
-
forbiddenActions: string[];
|
|
62
|
-
notes: string[];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export type ResolveIOAIManagerRecoveryCheckpointStatus =
|
|
66
|
-
| 'active'
|
|
67
|
-
| 'parked'
|
|
68
|
-
| 'manual_handoff'
|
|
69
|
-
| 'complete';
|
|
70
|
-
|
|
71
|
-
export interface ResolveIOAIManagerRecoveryCheckpoint {
|
|
72
|
-
checkpointId: string;
|
|
73
|
-
recoveryClass: ResolveIOAIManagerRecoveryClass;
|
|
74
|
-
status: ResolveIOAIManagerRecoveryCheckpointStatus;
|
|
75
|
-
lane: string;
|
|
76
|
-
stepType: string;
|
|
77
|
-
allowedAction: string;
|
|
78
|
-
productRepairAllowed: boolean;
|
|
79
|
-
expensiveModelAllowed: boolean;
|
|
80
|
-
attempts: number;
|
|
81
|
-
maxAttemptsBeforePark: number;
|
|
82
|
-
blockerFingerprint: string;
|
|
83
|
-
evidenceHash: string;
|
|
84
|
-
changedFiles: string[];
|
|
85
|
-
artifactPaths: string[];
|
|
86
|
-
requiredEvidence: string[];
|
|
87
|
-
loopResetEvidence: string[];
|
|
88
|
-
forbiddenActions: string[];
|
|
89
|
-
objective: string;
|
|
90
|
-
nextActionLabel: string;
|
|
91
|
-
createdAt: string;
|
|
92
|
-
updatedAt: string;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export type ResolveIOAIManagerRecoveryEvidenceProbeStepKind =
|
|
96
|
-
| 'read_only_diagnosis'
|
|
97
|
-
| 'rerun_same_gate'
|
|
98
|
-
| 'infra_preflight'
|
|
99
|
-
| 'compile_check'
|
|
100
|
-
| 'route_probe'
|
|
101
|
-
| 'business_assertion'
|
|
102
|
-
| 'journey_validation'
|
|
103
|
-
| 'release_status'
|
|
104
|
-
| 'diff_scope_check'
|
|
105
|
-
| 'operator_review';
|
|
106
|
-
|
|
107
|
-
export interface ResolveIOAIManagerRecoveryEvidenceProbeStep {
|
|
108
|
-
id: string;
|
|
109
|
-
kind: ResolveIOAIManagerRecoveryEvidenceProbeStepKind;
|
|
110
|
-
label: string;
|
|
111
|
-
objective: string;
|
|
112
|
-
required: boolean;
|
|
113
|
-
commandHint?: string;
|
|
114
|
-
artifactExpectation: string;
|
|
115
|
-
successSignal: string;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export interface ResolveIOAIManagerRecoveryEvidenceProbe {
|
|
119
|
-
probeId: string;
|
|
120
|
-
checkpointId: string;
|
|
121
|
-
recoveryClass: ResolveIOAIManagerRecoveryClass;
|
|
122
|
-
lane: string;
|
|
123
|
-
stepType: string;
|
|
124
|
-
objective: string;
|
|
125
|
-
evidenceOnly: boolean;
|
|
126
|
-
productRepairAllowed: boolean;
|
|
127
|
-
expensiveModelAllowed: boolean;
|
|
128
|
-
evidenceHashBefore: string;
|
|
129
|
-
blockerFingerprint: string;
|
|
130
|
-
steps: ResolveIOAIManagerRecoveryEvidenceProbeStep[];
|
|
131
|
-
requiredArtifacts: string[];
|
|
132
|
-
acceptanceEvidence: string[];
|
|
133
|
-
stopConditions: string[];
|
|
134
|
-
loopResetEvidence: string[];
|
|
135
|
-
createdAt: string;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export type ResolveIOAIManagerRecoveryAutomationMode =
|
|
139
|
-
| 'advance'
|
|
140
|
-
| 'collect_evidence'
|
|
141
|
-
| 'read_only_diagnosis'
|
|
142
|
-
| 'repair_infra'
|
|
143
|
-
| 'repair_compile'
|
|
144
|
-
| 'repair_journey_contract'
|
|
145
|
-
| 'repair_business_assertion'
|
|
146
|
-
| 'repair_release'
|
|
147
|
-
| 'targeted_product_repair'
|
|
148
|
-
| 'manual_review'
|
|
149
|
-
| 'continue_gate';
|
|
150
|
-
|
|
151
|
-
export interface ResolveIOAIManagerRecoveryActionRetryPolicy {
|
|
152
|
-
allowImmediateRetry: boolean;
|
|
153
|
-
requireNewEvidence: boolean;
|
|
154
|
-
resetLoopWhen: string[];
|
|
155
|
-
stopWhen: string[];
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export type ResolveIOAIManagerHotfixFirstReleaseAction =
|
|
159
|
-
| 'wait_for_active_deploy'
|
|
160
|
-
| 'block_duplicate_deploy'
|
|
161
|
-
| 'block_duplicate_publish'
|
|
162
|
-
| 'review_force_deploy'
|
|
163
|
-
| 'hotfix_release_artifact'
|
|
164
|
-
| 'deploy_new_artifact_after_release_gate'
|
|
165
|
-
| 'build_artifact_first';
|
|
166
|
-
|
|
167
|
-
export type ResolveIOAIManagerHotfixChannel =
|
|
168
|
-
| 'static_ui'
|
|
169
|
-
| 'backend_js'
|
|
170
|
-
| 'config'
|
|
171
|
-
| 'seed_data'
|
|
172
|
-
| 'cache_invalidation'
|
|
173
|
-
| 'service_restart'
|
|
174
|
-
| 'release_artifact'
|
|
175
|
-
| 'force_deploy_review'
|
|
176
|
-
| 'new_artifact_deploy'
|
|
177
|
-
| 'artifact_build';
|
|
178
|
-
|
|
179
|
-
export interface ResolveIOAIManagerHotfixPlanStep {
|
|
180
|
-
id: string;
|
|
181
|
-
label: string;
|
|
182
|
-
channel: ResolveIOAIManagerHotfixChannel;
|
|
183
|
-
safeToAutoRun: boolean;
|
|
184
|
-
requiresFullDeploy: boolean;
|
|
185
|
-
requiredEvidence: string[];
|
|
186
|
-
successEvidence: string[];
|
|
187
|
-
blockedBy: string[];
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export interface ResolveIOAIManagerHotfixPlan {
|
|
191
|
-
planId: string;
|
|
192
|
-
label: string;
|
|
193
|
-
recommendedChannel: ResolveIOAIManagerHotfixChannel;
|
|
194
|
-
reason: string;
|
|
195
|
-
fullDeployAvoided: boolean;
|
|
196
|
-
steps: ResolveIOAIManagerHotfixPlanStep[];
|
|
197
|
-
acceptanceEvidence: string[];
|
|
198
|
-
escalationTriggers: string[];
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export interface ResolveIOAIManagerHotfixFirstReleasePolicyInput {
|
|
202
|
-
surface?: string;
|
|
203
|
-
activeDeploy?: boolean;
|
|
204
|
-
deployFingerprint?: string;
|
|
205
|
-
lastDeployFingerprint?: string;
|
|
206
|
-
publishFingerprint?: string;
|
|
207
|
-
lastPublishFingerprint?: string;
|
|
208
|
-
deployStatus?: string;
|
|
209
|
-
publishStatus?: string;
|
|
210
|
-
sampleDataStatus?: string;
|
|
211
|
-
forceDeploy?: boolean;
|
|
212
|
-
forcePublish?: boolean;
|
|
213
|
-
hasNewArtifact?: boolean;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export interface ResolveIOAIManagerHotfixFirstReleasePolicy {
|
|
217
|
-
policy: 'hotfix_first';
|
|
218
|
-
surface: string;
|
|
219
|
-
recommendedAction: ResolveIOAIManagerHotfixFirstReleaseAction;
|
|
220
|
-
label: string;
|
|
221
|
-
reason: string;
|
|
222
|
-
hotfixPreferred: boolean;
|
|
223
|
-
fullDeployAllowed: boolean;
|
|
224
|
-
duplicateDeployBlocked: boolean;
|
|
225
|
-
duplicatePublishBlocked: boolean;
|
|
226
|
-
forceDeployRequiredToRepeat: boolean;
|
|
227
|
-
forcePublishRequiredToRepeat: boolean;
|
|
228
|
-
statuses: {
|
|
229
|
-
deploy: string;
|
|
230
|
-
publish: string;
|
|
231
|
-
sampleData: string;
|
|
232
|
-
};
|
|
233
|
-
fingerprints: {
|
|
234
|
-
current: string;
|
|
235
|
-
last: string;
|
|
236
|
-
matchesLast: boolean;
|
|
237
|
-
publishCurrent: string;
|
|
238
|
-
publishLast: string;
|
|
239
|
-
publishMatchesLast: boolean;
|
|
240
|
-
};
|
|
241
|
-
hotfixPlan: ResolveIOAIManagerHotfixPlan;
|
|
242
|
-
allowedActions: string[];
|
|
243
|
-
forbiddenActions: string[];
|
|
244
|
-
requiredEvidence: string[];
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export type ResolveIOAIManagerHotfixEvidenceStatus =
|
|
248
|
-
| 'missing'
|
|
249
|
-
| 'incomplete'
|
|
250
|
-
| 'passed'
|
|
251
|
-
| 'blocked';
|
|
252
|
-
|
|
253
|
-
export interface ResolveIOAIManagerHotfixEvidenceTarget {
|
|
254
|
-
surface?: string;
|
|
255
|
-
host?: string;
|
|
256
|
-
path?: string;
|
|
257
|
-
artifactPath?: string;
|
|
258
|
-
bucket?: string;
|
|
259
|
-
distributionId?: string;
|
|
260
|
-
processName?: string;
|
|
261
|
-
configKey?: string;
|
|
262
|
-
cacheKey?: string;
|
|
263
|
-
seedKey?: string;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export interface ResolveIOAIManagerHotfixEvidence {
|
|
267
|
-
channel: ResolveIOAIManagerHotfixChannel;
|
|
268
|
-
status?: ResolveIOAIManagerHotfixEvidenceStatus | string;
|
|
269
|
-
target: ResolveIOAIManagerHotfixEvidenceTarget;
|
|
270
|
-
compiledArtifactPath?: string;
|
|
271
|
-
builtDistPath?: string;
|
|
272
|
-
remoteChecksumBefore?: string;
|
|
273
|
-
remoteChecksumAfter?: string;
|
|
274
|
-
remoteChecksum?: string;
|
|
275
|
-
restartEvidence?: string;
|
|
276
|
-
healthCheckStatus?: string;
|
|
277
|
-
selfTestStatus?: string;
|
|
278
|
-
releaseGateStatus?: string;
|
|
279
|
-
s3UploadResult?: string;
|
|
280
|
-
cloudfrontInvalidationId?: string;
|
|
281
|
-
publicProof?: string;
|
|
282
|
-
configChangeEvidence?: string;
|
|
283
|
-
seedDataEvidence?: string;
|
|
284
|
-
cacheInvalidationEvidence?: string;
|
|
285
|
-
serviceRestartEvidence?: string;
|
|
286
|
-
releaseArtifactFingerprint?: string;
|
|
287
|
-
lastReleaseArtifactFingerprint?: string;
|
|
288
|
-
sourceCommitSha?: string;
|
|
289
|
-
githubCommitUrl?: string;
|
|
290
|
-
gitCommitStatus?: string;
|
|
291
|
-
gitPushStatus?: string;
|
|
292
|
-
committedAt?: Date | string;
|
|
293
|
-
forceDeployReason?: string;
|
|
294
|
-
hotfixCannotResolveReason?: string;
|
|
295
|
-
fullDeployRequested?: boolean;
|
|
296
|
-
recordedAt?: Date | string;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export interface ResolveIOAIManagerHotfixEvidenceValidation {
|
|
300
|
-
valid: boolean;
|
|
301
|
-
status: ResolveIOAIManagerHotfixEvidenceStatus;
|
|
302
|
-
channel: ResolveIOAIManagerHotfixChannel | '';
|
|
303
|
-
blockers: string[];
|
|
304
|
-
warnings: string[];
|
|
305
|
-
fullDeployAllowed: boolean;
|
|
306
|
-
fullDeployBlocked: boolean;
|
|
307
|
-
hotfixSatisfied: boolean;
|
|
308
|
-
requiredEvidence: string[];
|
|
309
|
-
successEvidence: string[];
|
|
310
|
-
normalized?: ResolveIOAIManagerHotfixEvidence;
|
|
311
|
-
githubCommitGuard: ResolveIOAIManagerHotfixGitProof;
|
|
312
|
-
policy?: ResolveIOAIManagerHotfixFirstReleasePolicy;
|
|
313
|
-
nextAction: 'record_hotfix_evidence' | 'rerun_release_gate' | 'request_force_deploy_reason' | 'allow_one_full_deploy' | 'park_manual';
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export type ResolveIOAIManagerHotfixGitProofStatus =
|
|
317
|
-
| 'not_required'
|
|
318
|
-
| 'missing'
|
|
319
|
-
| 'invalid'
|
|
320
|
-
| 'passed';
|
|
321
|
-
|
|
322
|
-
export interface ResolveIOAIManagerHotfixGitProof {
|
|
323
|
-
required: boolean;
|
|
324
|
-
status: ResolveIOAIManagerHotfixGitProofStatus;
|
|
325
|
-
passed: boolean;
|
|
326
|
-
managerMustCommitBeforeHotfix: boolean;
|
|
327
|
-
channel: ResolveIOAIManagerHotfixChannel | '';
|
|
328
|
-
sourceCommitSha: string;
|
|
329
|
-
githubCommitUrl: string;
|
|
330
|
-
githubCommitSha: string;
|
|
331
|
-
gitCommitStatus: string;
|
|
332
|
-
gitPushStatus: string;
|
|
333
|
-
blockers: string[];
|
|
334
|
-
nextCommands: string[];
|
|
335
|
-
requiredEvidence: string[];
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
export type ResolveIOAIManagerHotfixContinuationAction =
|
|
339
|
-
| 'record_hotfix_evidence'
|
|
340
|
-
| 'rerun_release_gate'
|
|
341
|
-
| 'continue_runner'
|
|
342
|
-
| 'request_force_deploy_reason'
|
|
343
|
-
| 'allow_one_full_deploy'
|
|
344
|
-
| 'park_manual';
|
|
345
|
-
|
|
346
|
-
export interface ResolveIOAIManagerHotfixContinuationInput {
|
|
347
|
-
evidence?: any;
|
|
348
|
-
policy?: ResolveIOAIManagerHotfixFirstReleasePolicy;
|
|
349
|
-
releaseGatePassed?: boolean;
|
|
350
|
-
repeatedFailure?: boolean;
|
|
351
|
-
failureClass?: string;
|
|
352
|
-
blocker?: string;
|
|
353
|
-
now?: Date | string;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
export interface ResolveIOAIManagerHotfixContinuationDecision {
|
|
357
|
-
action: ResolveIOAIManagerHotfixContinuationAction;
|
|
358
|
-
canContinueRun: boolean;
|
|
359
|
-
canRunFullDeploy: boolean;
|
|
360
|
-
shouldRerunReleaseGate: boolean;
|
|
361
|
-
shouldPark: boolean;
|
|
362
|
-
reason: string;
|
|
363
|
-
blockers: string[];
|
|
364
|
-
warnings: string[];
|
|
365
|
-
requiredEvidence: string[];
|
|
366
|
-
successEvidence: string[];
|
|
367
|
-
nextCommands: string[];
|
|
368
|
-
failureClass: string;
|
|
369
|
-
blockerFingerprint: string;
|
|
370
|
-
githubCommitGuard: ResolveIOAIManagerHotfixGitProof;
|
|
371
|
-
validation: ResolveIOAIManagerHotfixEvidenceValidation;
|
|
372
|
-
recordedAt: string;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
export interface ResolveIOAIManagerHotfixCommitGuardDecision {
|
|
376
|
-
active: boolean;
|
|
377
|
-
blocked: boolean;
|
|
378
|
-
reason: string;
|
|
379
|
-
nextAction: 'record_hotfix_evidence' | 'continue';
|
|
380
|
-
channel: ResolveIOAIManagerHotfixChannel | '';
|
|
381
|
-
blockers: string[];
|
|
382
|
-
nextCommands: string[];
|
|
383
|
-
requiredEvidence: string[];
|
|
384
|
-
githubCommitGuard: ResolveIOAIManagerHotfixGitProof;
|
|
385
|
-
validation: ResolveIOAIManagerHotfixEvidenceValidation;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
export type ResolveIOAIManagerHotfixEvidenceRecordSource =
|
|
389
|
-
| 'manual'
|
|
390
|
-
| 'manager'
|
|
391
|
-
| 'runner_console'
|
|
392
|
-
| 'hotfix_script'
|
|
393
|
-
| 'deploy_script'
|
|
394
|
-
| 'unknown';
|
|
395
|
-
|
|
396
|
-
export interface ResolveIOAIManagerHotfixEvidenceRecordInput extends Partial<ResolveIOAIManagerHotfixEvidence> {
|
|
397
|
-
evidence?: any;
|
|
398
|
-
policy?: ResolveIOAIManagerHotfixFirstReleasePolicy;
|
|
399
|
-
recordId?: string;
|
|
400
|
-
reason?: string;
|
|
401
|
-
recordedBy?: string;
|
|
402
|
-
source?: ResolveIOAIManagerHotfixEvidenceRecordSource | string;
|
|
403
|
-
now?: Date | string;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
export interface ResolveIOAIManagerHotfixEvidenceRecord {
|
|
407
|
-
recordId: string;
|
|
408
|
-
recordedAt: string;
|
|
409
|
-
recordedBy: string;
|
|
410
|
-
source: ResolveIOAIManagerHotfixEvidenceRecordSource | string;
|
|
411
|
-
reason: string;
|
|
412
|
-
channel: ResolveIOAIManagerHotfixChannel | '';
|
|
413
|
-
status: ResolveIOAIManagerHotfixEvidenceStatus;
|
|
414
|
-
nextAction: ResolveIOAIManagerHotfixEvidenceValidation['nextAction'];
|
|
415
|
-
valid: boolean;
|
|
416
|
-
hotfixSatisfied: boolean;
|
|
417
|
-
fullDeployAllowed: boolean;
|
|
418
|
-
fullDeployBlocked: boolean;
|
|
419
|
-
requiredFields: string[];
|
|
420
|
-
missingFields: string[];
|
|
421
|
-
evidence: ResolveIOAIManagerHotfixEvidence | undefined;
|
|
422
|
-
validation: ResolveIOAIManagerHotfixEvidenceValidation;
|
|
423
|
-
sourceCommitSha: string;
|
|
424
|
-
githubCommitUrl: string;
|
|
425
|
-
gitPushStatus: string;
|
|
426
|
-
githubCommitRequired: boolean;
|
|
427
|
-
githubCommitProofPassed: boolean;
|
|
428
|
-
commitProofStatus: 'not_required' | 'missing' | 'invalid' | 'passed';
|
|
429
|
-
managerMustCommitBeforeHotfix: boolean;
|
|
430
|
-
githubCommitGuard: ResolveIOAIManagerHotfixGitProof;
|
|
431
|
-
readyForReleaseGate: boolean;
|
|
432
|
-
readyForContinuation: boolean;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
export interface ResolveIOAIManagerRecoveryActionPacket {
|
|
436
|
-
actionId: string;
|
|
437
|
-
checkpointId: string;
|
|
438
|
-
probeId: string;
|
|
439
|
-
recoveryClass: ResolveIOAIManagerRecoveryClass;
|
|
440
|
-
mode: ResolveIOAIManagerRecoveryAutomationMode;
|
|
441
|
-
singleActionOnly: boolean;
|
|
442
|
-
allowedDispatchAction: ResolveIOAIManagerRecoveryActionDispatchAction;
|
|
443
|
-
label: string;
|
|
444
|
-
lane: string;
|
|
445
|
-
stepType: string;
|
|
446
|
-
primaryStepKind: ResolveIOAIManagerRecoveryEvidenceProbeStepKind | 'none';
|
|
447
|
-
objective: string;
|
|
448
|
-
evidenceOnly: boolean;
|
|
449
|
-
autoRunnable: boolean;
|
|
450
|
-
productRepairAllowed: boolean;
|
|
451
|
-
expensiveModelAllowed: boolean;
|
|
452
|
-
costCeilingUsd: number;
|
|
453
|
-
requiredStateTransition: string;
|
|
454
|
-
proofRequiredBeforeContinuation: boolean;
|
|
455
|
-
canResetLoopAfterEvidence: boolean;
|
|
456
|
-
maxAttemptsBeforePark: number;
|
|
457
|
-
requiredArtifacts: string[];
|
|
458
|
-
nextCommands: string[];
|
|
459
|
-
successCriteria: string[];
|
|
460
|
-
exitCriteria: string[];
|
|
461
|
-
retryPolicy: ResolveIOAIManagerRecoveryActionRetryPolicy;
|
|
462
|
-
releasePolicy?: ResolveIOAIManagerHotfixFirstReleasePolicy;
|
|
463
|
-
blockedReason?: string;
|
|
464
|
-
createdAt: string;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
export type ResolveIOAIManagerRecoveryActionDispatchAction =
|
|
468
|
-
| 'run_evidence_probe'
|
|
469
|
-
| 'run_read_only_diagnosis'
|
|
470
|
-
| 'run_infra_repair'
|
|
471
|
-
| 'run_compile_repair'
|
|
472
|
-
| 'run_journey_contract_repair'
|
|
473
|
-
| 'run_business_assertion_repair'
|
|
474
|
-
| 'run_release_repair'
|
|
475
|
-
| 'run_targeted_product_repair'
|
|
476
|
-
| 'advance'
|
|
477
|
-
| 'continue_gate'
|
|
478
|
-
| 'park_manual';
|
|
479
|
-
|
|
480
|
-
export interface ResolveIOAIManagerAutoDispatchPolicyInput {
|
|
481
|
-
dispatchAction?: ResolveIOAIManagerRecoveryActionDispatchAction | string;
|
|
482
|
-
directive?: Partial<ResolveIOAIManagerRecoveryExecutionDirective>;
|
|
483
|
-
action?: Partial<ResolveIOAIManagerRecoveryActionPacket>;
|
|
484
|
-
includeJourneyContractRepair?: boolean;
|
|
485
|
-
includeReleaseRepair?: boolean;
|
|
486
|
-
includeBusinessProofRepair?: boolean;
|
|
487
|
-
includeProductRepair?: boolean;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
export type ResolveIOAIManagerRecoveryActionDispatchStatus =
|
|
491
|
-
| 'queued'
|
|
492
|
-
| 'started'
|
|
493
|
-
| 'completed'
|
|
494
|
-
| 'failed'
|
|
495
|
-
| 'parked';
|
|
496
|
-
|
|
497
|
-
export interface ResolveIOAIManagerRecoveryActionDispatchRecord {
|
|
498
|
-
dispatchId: string;
|
|
499
|
-
actionId: string;
|
|
500
|
-
checkpointId: string;
|
|
501
|
-
probeId: string;
|
|
502
|
-
mode: ResolveIOAIManagerRecoveryAutomationMode;
|
|
503
|
-
dispatchAction: ResolveIOAIManagerRecoveryActionDispatchAction;
|
|
504
|
-
status: ResolveIOAIManagerRecoveryActionDispatchStatus;
|
|
505
|
-
evidenceHash: string;
|
|
506
|
-
blockerFingerprint: string;
|
|
507
|
-
productRepairAllowed: boolean;
|
|
508
|
-
expensiveModelAllowed: boolean;
|
|
509
|
-
reason: string;
|
|
510
|
-
artifactPaths: string[];
|
|
511
|
-
startedAt?: string;
|
|
512
|
-
completedAt?: string;
|
|
513
|
-
createdAt: string;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
export interface ResolveIOAIManagerRecoveryActionDispatchInput {
|
|
517
|
-
action?: ResolveIOAIManagerRecoveryActionPacket;
|
|
518
|
-
history?: ResolveIOAIManagerRecoveryActionDispatchRecord[];
|
|
519
|
-
current?: ResolveIOAIManagerFailureRecord;
|
|
520
|
-
autonomyPolicy?: ResolveIOAIManagerAutonomyPolicyInput;
|
|
521
|
-
operatorApproved?: boolean;
|
|
522
|
-
now?: Date | string;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
export interface ResolveIOAIManagerRecoveryActionDispatchDecision {
|
|
526
|
-
dispatchAction: ResolveIOAIManagerRecoveryActionDispatchAction;
|
|
527
|
-
allowed: boolean;
|
|
528
|
-
reason: string;
|
|
529
|
-
status: ResolveIOAIManagerRecoveryActionDispatchStatus;
|
|
530
|
-
canRunProductRepair: boolean;
|
|
531
|
-
canRunExpensiveModel: boolean;
|
|
532
|
-
shouldRecordDispatch: boolean;
|
|
533
|
-
requiresNewEvidence: boolean;
|
|
534
|
-
newEvidence: boolean;
|
|
535
|
-
autonomyPolicy?: ResolveIOAIManagerAutonomyPolicyDecision;
|
|
536
|
-
dispatchRecord?: ResolveIOAIManagerRecoveryActionDispatchRecord;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
export type ResolveIOAIManagerAutonomyMode =
|
|
540
|
-
| 'manual_only'
|
|
541
|
-
| 'monitor_only'
|
|
542
|
-
| 'auto_retry_within_budget';
|
|
543
|
-
|
|
544
|
-
export interface ResolveIOAIManagerAutonomyPolicyInput {
|
|
545
|
-
mode?: ResolveIOAIManagerAutonomyMode | string;
|
|
546
|
-
currentSpendUsd?: number;
|
|
547
|
-
budgetSoftUsd?: number;
|
|
548
|
-
budgetHardUsd?: number;
|
|
549
|
-
projectedActionCostUsd?: number;
|
|
550
|
-
dispatchAction?: ResolveIOAIManagerRecoveryActionDispatchAction | string;
|
|
551
|
-
expectedValueScore?: number;
|
|
552
|
-
minExpectedValueScore?: number;
|
|
553
|
-
evidenceStrength?: ResolveIOAIManagerEvidenceStrength | string;
|
|
554
|
-
materialEvidence?: boolean;
|
|
555
|
-
newEvidence?: boolean;
|
|
556
|
-
evidenceSignals?: string[];
|
|
557
|
-
sameEvidenceAlreadyAttempted?: boolean;
|
|
558
|
-
allowEvidenceProbeWithoutExpectedValue?: boolean;
|
|
559
|
-
operatorApproved?: boolean;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
export interface ResolveIOAIManagerRetryExpectedValueInput {
|
|
563
|
-
dispatchAction?: ResolveIOAIManagerRecoveryActionDispatchAction | string;
|
|
564
|
-
projectedActionCostUsd?: number;
|
|
565
|
-
currentSpendUsd?: number;
|
|
566
|
-
budgetHardUsd?: number;
|
|
567
|
-
failureClass?: string;
|
|
568
|
-
recoveryClass?: ResolveIOAIManagerRecoveryClass | string;
|
|
569
|
-
evidenceStrength?: ResolveIOAIManagerEvidenceStrength | string;
|
|
570
|
-
materialEvidence?: boolean;
|
|
571
|
-
newEvidence?: boolean;
|
|
572
|
-
evidenceSignals?: string[];
|
|
573
|
-
sameEvidenceAlreadyAttempted?: boolean;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
export interface ResolveIOAIManagerRetryExpectedValueEstimate {
|
|
577
|
-
score: number;
|
|
578
|
-
minScore: number;
|
|
579
|
-
positive: boolean;
|
|
580
|
-
source: 'estimated';
|
|
581
|
-
confidence: 'low' | 'medium' | 'high';
|
|
582
|
-
reason: string;
|
|
583
|
-
signals: string[];
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
export interface ResolveIOAIManagerAutonomyPolicyDecision {
|
|
587
|
-
mode: ResolveIOAIManagerAutonomyMode;
|
|
588
|
-
canAutoDispatch: boolean;
|
|
589
|
-
canManualDispatch: boolean;
|
|
590
|
-
blocked: boolean;
|
|
591
|
-
requiresHumanApproval: boolean;
|
|
592
|
-
reason: string;
|
|
593
|
-
currentSpendUsd: number;
|
|
594
|
-
projectedActionCostUsd: number;
|
|
595
|
-
projectedSpendUsd: number;
|
|
596
|
-
budgetSoftUsd: number;
|
|
597
|
-
budgetHardUsd: number;
|
|
598
|
-
softBudgetExceeded: boolean;
|
|
599
|
-
hardBudgetExceeded: boolean;
|
|
600
|
-
dispatchAction: string;
|
|
601
|
-
expectedValueScore: number;
|
|
602
|
-
minExpectedValueScore: number;
|
|
603
|
-
expectedValueKnown: boolean;
|
|
604
|
-
expectedValueSource: 'provided' | 'estimated';
|
|
605
|
-
expectedValuePositive: boolean;
|
|
606
|
-
evidenceStrength: ResolveIOAIManagerEvidenceStrength;
|
|
607
|
-
materialEvidence: boolean;
|
|
608
|
-
newEvidence: boolean;
|
|
609
|
-
evidenceBacked: boolean;
|
|
610
|
-
evidenceSignals: string[];
|
|
611
|
-
recommendedControl: 'manual_only' | 'monitor_only' | 'auto_retry_within_budget' | 'increase_budget_or_manual';
|
|
612
|
-
forbiddenActions: string[];
|
|
613
|
-
requiredEvidence: string[];
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
export type ResolveIOAIManagerRecoveryExecutionPhase =
|
|
617
|
-
| 'evidence'
|
|
618
|
-
| 'diagnosis'
|
|
619
|
-
| 'infra'
|
|
620
|
-
| 'compile'
|
|
621
|
-
| 'journey'
|
|
622
|
-
| 'business_proof'
|
|
623
|
-
| 'release'
|
|
624
|
-
| 'product_repair'
|
|
625
|
-
| 'advance'
|
|
626
|
-
| 'manual';
|
|
627
|
-
|
|
628
|
-
export interface ResolveIOAIManagerRecoveryExecutionDirectiveInput {
|
|
629
|
-
action?: ResolveIOAIManagerRecoveryActionPacket;
|
|
630
|
-
dispatchDecision?: ResolveIOAIManagerRecoveryActionDispatchDecision;
|
|
631
|
-
current?: ResolveIOAIManagerFailureRecord;
|
|
632
|
-
autonomyPolicy?: ResolveIOAIManagerAutonomyPolicyInput;
|
|
633
|
-
surface?: string;
|
|
634
|
-
now?: Date | string;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
export interface ResolveIOAIManagerRecoveryExecutionDirective {
|
|
638
|
-
directiveId: string;
|
|
639
|
-
surface: string;
|
|
640
|
-
dispatchAction: ResolveIOAIManagerRecoveryActionDispatchAction;
|
|
641
|
-
phase: ResolveIOAIManagerRecoveryExecutionPhase;
|
|
642
|
-
allowed: boolean;
|
|
643
|
-
status: ResolveIOAIManagerRecoveryActionDispatchStatus;
|
|
644
|
-
reason: string;
|
|
645
|
-
singleActionOnly: boolean;
|
|
646
|
-
allowedDispatchAction: ResolveIOAIManagerRecoveryActionDispatchAction;
|
|
647
|
-
lane: string;
|
|
648
|
-
stepType: string;
|
|
649
|
-
nextActionLabel: string;
|
|
650
|
-
rerunReason: string;
|
|
651
|
-
evidenceOnly: boolean;
|
|
652
|
-
autoRunnable: boolean;
|
|
653
|
-
productRepairAllowed: boolean;
|
|
654
|
-
expensiveModelAllowed: boolean;
|
|
655
|
-
costCeilingUsd: number;
|
|
656
|
-
requiredStateTransition: string;
|
|
657
|
-
proofRequiredBeforeContinuation: boolean;
|
|
658
|
-
canRunProductRepair: boolean;
|
|
659
|
-
canRunExpensiveModel: boolean;
|
|
660
|
-
canResetLoopAfterEvidence: boolean;
|
|
661
|
-
requiresNewEvidence: boolean;
|
|
662
|
-
newEvidence: boolean;
|
|
663
|
-
autonomyPolicy?: ResolveIOAIManagerAutonomyPolicyDecision;
|
|
664
|
-
maxAttemptsBeforePark: number;
|
|
665
|
-
requiredArtifacts: string[];
|
|
666
|
-
nextCommands: string[];
|
|
667
|
-
successCriteria: string[];
|
|
668
|
-
exitCriteria: string[];
|
|
669
|
-
forbiddenActions: string[];
|
|
670
|
-
dispatchRecord?: ResolveIOAIManagerRecoveryActionDispatchRecord;
|
|
671
|
-
releasePolicy?: ResolveIOAIManagerHotfixFirstReleasePolicy;
|
|
672
|
-
createdAt: string;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
export interface ResolveIOAIManagerRecoveryCheckpointInput {
|
|
676
|
-
plan: ResolveIOAIManagerRecoveryPlan;
|
|
677
|
-
current?: ResolveIOAIManagerFailureRecord;
|
|
678
|
-
previousCheckpoint?: ResolveIOAIManagerRecoveryCheckpoint;
|
|
679
|
-
now?: Date | string;
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
export type ResolveIOAIManagerRecoveryGateAction =
|
|
683
|
-
| 'allow'
|
|
684
|
-
| 'collect_new_evidence'
|
|
685
|
-
| 'reject_action'
|
|
686
|
-
| 'manual_handoff'
|
|
687
|
-
| 'complete';
|
|
688
|
-
|
|
689
|
-
export interface ResolveIOAIManagerRecoveryGateInput {
|
|
690
|
-
checkpoint: ResolveIOAIManagerRecoveryCheckpoint;
|
|
691
|
-
current?: ResolveIOAIManagerFailureRecord;
|
|
692
|
-
proposedAction?: string;
|
|
693
|
-
operatorApproved?: boolean;
|
|
694
|
-
now?: Date | string;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
export interface ResolveIOAIManagerRecoveryGateDecision {
|
|
698
|
-
action: ResolveIOAIManagerRecoveryGateAction;
|
|
699
|
-
reason: string;
|
|
700
|
-
canRunProductRepair: boolean;
|
|
701
|
-
canRunExpensiveModel: boolean;
|
|
702
|
-
shouldResetLoopBudget: boolean;
|
|
703
|
-
shouldIncrementAttempt: boolean;
|
|
704
|
-
newEvidence: boolean;
|
|
705
|
-
materialEvidence: boolean;
|
|
706
|
-
evidenceStrength: ResolveIOAIManagerEvidenceStrength;
|
|
707
|
-
evidenceSignals: string[];
|
|
708
|
-
blockerFingerprint: string;
|
|
709
|
-
evidenceHash: string;
|
|
710
|
-
missingEvidence: string[];
|
|
711
|
-
checkpoint: ResolveIOAIManagerRecoveryCheckpoint;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
export interface ResolveIOAIManagerRecoveryEvidenceProbeInput {
|
|
715
|
-
checkpoint: ResolveIOAIManagerRecoveryCheckpoint;
|
|
716
|
-
current?: ResolveIOAIManagerFailureRecord;
|
|
717
|
-
now?: Date | string;
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
export interface ResolveIOAIManagerRecoveryActionPacketInput {
|
|
721
|
-
plan?: ResolveIOAIManagerRecoveryPlan;
|
|
722
|
-
checkpoint: ResolveIOAIManagerRecoveryCheckpoint;
|
|
723
|
-
probe?: ResolveIOAIManagerRecoveryEvidenceProbe;
|
|
724
|
-
current?: ResolveIOAIManagerFailureRecord;
|
|
725
|
-
now?: Date | string;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
export interface ResolveIOAIManagerRecoveryPlanInput {
|
|
729
|
-
action?: ResolveIOAIManagerAction | string;
|
|
730
|
-
reason?: string;
|
|
731
|
-
failureClass?: string;
|
|
732
|
-
lane?: string;
|
|
733
|
-
stepType?: string;
|
|
734
|
-
blocker?: string;
|
|
735
|
-
sameFailureCount?: number;
|
|
736
|
-
pingPongCount?: number;
|
|
737
|
-
newEvidence?: boolean;
|
|
738
|
-
productRepairFailure?: boolean;
|
|
739
|
-
changedFiles?: string[];
|
|
740
|
-
artifactPaths?: string[];
|
|
741
|
-
maxSameFailureRepeats?: number;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
export interface ResolveIOAIManagerPolicyInput {
|
|
745
|
-
history?: ResolveIOAIManagerFailureRecord[];
|
|
746
|
-
current?: ResolveIOAIManagerFailureRecord;
|
|
747
|
-
maxSameFailureRepeats?: number;
|
|
748
|
-
maxPingPongTransitions?: number;
|
|
749
|
-
infraFailureClasses?: string[];
|
|
750
|
-
ignoredFailureClasses?: string[];
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
export interface ResolveIOAIManagerPolicyDecision {
|
|
754
|
-
action: ResolveIOAIManagerAction;
|
|
755
|
-
reason: string;
|
|
756
|
-
failureClass: string;
|
|
757
|
-
blockerFingerprint: string;
|
|
758
|
-
evidenceHash: string;
|
|
759
|
-
sameFailureCount: number;
|
|
760
|
-
pingPongCount: number;
|
|
761
|
-
newEvidence: boolean;
|
|
762
|
-
materialEvidence: boolean;
|
|
763
|
-
evidenceStrength: ResolveIOAIManagerEvidenceStrength;
|
|
764
|
-
evidenceSignals: string[];
|
|
765
|
-
loopBudgetShouldReset: boolean;
|
|
766
|
-
productRepairFailure: boolean;
|
|
767
|
-
recoveryPlan: ResolveIOAIManagerRecoveryPlan;
|
|
768
|
-
recoveryCheckpoint: ResolveIOAIManagerRecoveryCheckpoint;
|
|
769
|
-
recoveryEvidenceProbe: ResolveIOAIManagerRecoveryEvidenceProbe;
|
|
770
|
-
recoveryAction: ResolveIOAIManagerRecoveryActionPacket;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
export interface ResolveIOAIManagerRecoveryReplayMatrixInput {
|
|
774
|
-
surface?: string;
|
|
775
|
-
includeJourneyContractRepair?: boolean;
|
|
776
|
-
includeReleaseRepair?: boolean;
|
|
777
|
-
includeProductRepair?: boolean;
|
|
778
|
-
maxSameFailureRepeats?: number;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
export interface ResolveIOAIManagerRecoveryReplayCase {
|
|
782
|
-
caseId: string;
|
|
783
|
-
surface: string;
|
|
784
|
-
pass: boolean;
|
|
785
|
-
action: string;
|
|
786
|
-
reason: string;
|
|
787
|
-
recoveryClass: string;
|
|
788
|
-
dispatchAction: string;
|
|
789
|
-
safeAutoDispatch: boolean;
|
|
790
|
-
expectedSafeAutoDispatch: boolean;
|
|
791
|
-
productRepairAllowed: boolean;
|
|
792
|
-
loopBudgetShouldReset: boolean;
|
|
793
|
-
materialEvidence: boolean;
|
|
794
|
-
evidenceStrength: ResolveIOAIManagerEvidenceStrength;
|
|
795
|
-
hotfixFirst?: boolean;
|
|
796
|
-
fullDeployAllowed?: boolean;
|
|
797
|
-
details?: Record<string, any>;
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
function cleanText(value: any, max = 2000): string {
|
|
801
|
-
return String(value || '').replace(/\s+/g, ' ').trim().slice(0, max);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
function cleanList(values: any, limit = 40, max = 500): string[] {
|
|
805
|
-
if (!Array.isArray(values)) {
|
|
806
|
-
return [];
|
|
807
|
-
}
|
|
808
|
-
const result: string[] = [];
|
|
809
|
-
for (const value of values) {
|
|
810
|
-
const normalized = cleanText(value, max);
|
|
811
|
-
if (normalized && !result.includes(normalized)) {
|
|
812
|
-
result.push(normalized);
|
|
813
|
-
}
|
|
814
|
-
if (result.length >= limit) {
|
|
815
|
-
break;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
return result;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
const HOTFIX_CHANNELS = new Set<ResolveIOAIManagerHotfixChannel>([
|
|
822
|
-
'static_ui',
|
|
823
|
-
'backend_js',
|
|
824
|
-
'config',
|
|
825
|
-
'seed_data',
|
|
826
|
-
'cache_invalidation',
|
|
827
|
-
'service_restart',
|
|
828
|
-
'release_artifact',
|
|
829
|
-
'force_deploy_review',
|
|
830
|
-
'new_artifact_deploy',
|
|
831
|
-
'artifact_build'
|
|
832
|
-
]);
|
|
833
|
-
|
|
834
|
-
function cleanObject(value: any): Record<string, any> {
|
|
835
|
-
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
function normalizeAutonomyMode(value: any): ResolveIOAIManagerAutonomyMode {
|
|
839
|
-
const normalized = cleanText(value, 120).toLowerCase().replace(/[\s-]+/g, '_');
|
|
840
|
-
if (/^(monitor|monitor_only|watch|watch_only)$/.test(normalized)) {
|
|
841
|
-
return 'monitor_only';
|
|
842
|
-
}
|
|
843
|
-
if (/^(auto|auto_retry|auto_retry_within_budget|autonomous|within_budget)$/.test(normalized)) {
|
|
844
|
-
return 'auto_retry_within_budget';
|
|
845
|
-
}
|
|
846
|
-
return 'manual_only';
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
function normalizeManagerEvidenceStrength(value: any): ResolveIOAIManagerEvidenceStrength {
|
|
850
|
-
const normalized = cleanText(value, 40).toLowerCase().replace(/[\s-]+/g, '_');
|
|
851
|
-
if (normalized === 'proof') {
|
|
852
|
-
return 'proof';
|
|
853
|
-
}
|
|
854
|
-
if (normalized === 'material' || normalized === 'strong') {
|
|
855
|
-
return 'material';
|
|
856
|
-
}
|
|
857
|
-
if (normalized === 'weak') {
|
|
858
|
-
return 'weak';
|
|
859
|
-
}
|
|
860
|
-
return 'none';
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
function nonNegativeMoney(value: any): number {
|
|
864
|
-
const parsed = Number(value);
|
|
865
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
function finiteNumber(value: any): number | undefined {
|
|
869
|
-
const parsed = Number(value);
|
|
870
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
function clampNumber(value: number, min: number, max: number): number {
|
|
874
|
-
return Math.min(max, Math.max(min, value));
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
function isCheapEvidenceAutonomyAction(value: any): boolean {
|
|
878
|
-
const action = cleanText(value, 120);
|
|
879
|
-
return !action || /^(run_evidence_probe|run_read_only_diagnosis|advance|continue_gate|park_manual)$/.test(action);
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
export function estimateResolveIOAIManagerRetryExpectedValue(
|
|
883
|
-
input: ResolveIOAIManagerRetryExpectedValueInput = {}
|
|
884
|
-
): ResolveIOAIManagerRetryExpectedValueEstimate {
|
|
885
|
-
const dispatchAction = cleanText(input.dispatchAction, 120);
|
|
886
|
-
const failureClass = normalizeResolveIOAIManagerFailureClass(input.failureClass);
|
|
887
|
-
const recoveryClass = cleanText(input.recoveryClass, 120);
|
|
888
|
-
const evidenceStrength = normalizeManagerEvidenceStrength(input.evidenceStrength);
|
|
889
|
-
const evidenceSignals = cleanList(input.evidenceSignals, 12, 120);
|
|
890
|
-
const projectedActionCostUsd = nonNegativeMoney(input.projectedActionCostUsd);
|
|
891
|
-
const budgetHardUsd = nonNegativeMoney(input.budgetHardUsd);
|
|
892
|
-
const materialEvidence = input.materialEvidence === true || evidenceStrength === 'material' || evidenceStrength === 'proof';
|
|
893
|
-
const newEvidence = input.newEvidence === true;
|
|
894
|
-
const signals: string[] = [];
|
|
895
|
-
let score = 0.1;
|
|
896
|
-
if (dispatchAction === 'run_evidence_probe') {
|
|
897
|
-
score = 0.75;
|
|
898
|
-
signals.push('cheap_evidence_probe');
|
|
899
|
-
}
|
|
900
|
-
else if (dispatchAction === 'run_read_only_diagnosis') {
|
|
901
|
-
score = 0.65;
|
|
902
|
-
signals.push('read_only_diagnosis');
|
|
903
|
-
}
|
|
904
|
-
else if (dispatchAction === 'advance' || dispatchAction === 'continue_gate') {
|
|
905
|
-
score = 0.55;
|
|
906
|
-
signals.push('gate_continuation');
|
|
907
|
-
}
|
|
908
|
-
else if (dispatchAction === 'run_infra_repair') {
|
|
909
|
-
score = 0.4;
|
|
910
|
-
signals.push('bounded_infra_repair');
|
|
911
|
-
}
|
|
912
|
-
else if (dispatchAction === 'run_compile_repair') {
|
|
913
|
-
score = 0.4;
|
|
914
|
-
signals.push('bounded_compile_repair');
|
|
915
|
-
}
|
|
916
|
-
else if (dispatchAction === 'run_release_repair') {
|
|
917
|
-
score = 0.3;
|
|
918
|
-
signals.push('bounded_release_repair');
|
|
919
|
-
}
|
|
920
|
-
else if (dispatchAction === 'run_journey_contract_repair' || dispatchAction === 'run_business_assertion_repair') {
|
|
921
|
-
score = 0.25;
|
|
922
|
-
signals.push('bounded_workflow_or_proof_repair');
|
|
923
|
-
}
|
|
924
|
-
else if (dispatchAction === 'run_targeted_product_repair') {
|
|
925
|
-
score = 0.15;
|
|
926
|
-
signals.push('targeted_product_repair');
|
|
927
|
-
}
|
|
928
|
-
if (evidenceStrength === 'proof') {
|
|
929
|
-
score += 0.3;
|
|
930
|
-
signals.push('proof_evidence');
|
|
931
|
-
}
|
|
932
|
-
else if (materialEvidence) {
|
|
933
|
-
score += 0.2;
|
|
934
|
-
signals.push('material_evidence');
|
|
935
|
-
}
|
|
936
|
-
else if (newEvidence) {
|
|
937
|
-
score += 0.1;
|
|
938
|
-
signals.push('new_evidence');
|
|
939
|
-
}
|
|
940
|
-
else if (evidenceStrength === 'weak') {
|
|
941
|
-
score -= 0.15;
|
|
942
|
-
signals.push('weak_evidence');
|
|
943
|
-
}
|
|
944
|
-
else if (!isCheapEvidenceAutonomyAction(dispatchAction)) {
|
|
945
|
-
score -= 0.25;
|
|
946
|
-
signals.push('no_material_evidence');
|
|
947
|
-
}
|
|
948
|
-
if ((failureClass === 'infra' || /infra/i.test(recoveryClass)) && dispatchAction === 'run_infra_repair') {
|
|
949
|
-
score += 0.15;
|
|
950
|
-
signals.push('failure_matches_infra_repair');
|
|
951
|
-
}
|
|
952
|
-
if ((failureClass === 'compile' || /compile/i.test(recoveryClass)) && dispatchAction === 'run_compile_repair') {
|
|
953
|
-
score += 0.15;
|
|
954
|
-
signals.push('failure_matches_compile_repair');
|
|
955
|
-
}
|
|
956
|
-
if (/release|publish|deploy/i.test(`${failureClass} ${recoveryClass}`) && dispatchAction === 'run_release_repair') {
|
|
957
|
-
score += 0.1;
|
|
958
|
-
signals.push('failure_matches_release_repair');
|
|
959
|
-
}
|
|
960
|
-
if (projectedActionCostUsd >= 5) {
|
|
961
|
-
score -= 0.3;
|
|
962
|
-
signals.push('high_projected_cost');
|
|
963
|
-
}
|
|
964
|
-
else if (projectedActionCostUsd >= 2) {
|
|
965
|
-
score -= 0.18;
|
|
966
|
-
signals.push('medium_projected_cost');
|
|
967
|
-
}
|
|
968
|
-
else if (projectedActionCostUsd >= 1) {
|
|
969
|
-
score -= 0.08;
|
|
970
|
-
signals.push('low_projected_cost');
|
|
971
|
-
}
|
|
972
|
-
if (budgetHardUsd > 0 && projectedActionCostUsd > budgetHardUsd * 0.5) {
|
|
973
|
-
score -= 0.2;
|
|
974
|
-
signals.push('large_fraction_of_remaining_budget');
|
|
975
|
-
}
|
|
976
|
-
if (input.sameEvidenceAlreadyAttempted === true) {
|
|
977
|
-
score -= 0.35;
|
|
978
|
-
signals.push('same_evidence_already_attempted');
|
|
979
|
-
}
|
|
980
|
-
for (const signal of evidenceSignals) {
|
|
981
|
-
if (/passing_outcome|business|proof|diagnosis|compile|infra|release|artifact|fingerprint_changed|evidence_hash_changed/i.test(signal)) {
|
|
982
|
-
score += 0.03;
|
|
983
|
-
signals.push(`evidence_signal:${signal}`);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
const rounded = Number(clampNumber(score, -1, 1).toFixed(2));
|
|
987
|
-
const minScore = 0;
|
|
988
|
-
return {
|
|
989
|
-
score: rounded,
|
|
990
|
-
minScore,
|
|
991
|
-
positive: rounded >= minScore,
|
|
992
|
-
source: 'estimated',
|
|
993
|
-
confidence: evidenceStrength === 'proof' || materialEvidence ? 'medium' : 'low',
|
|
994
|
-
reason: rounded >= minScore
|
|
995
|
-
? 'estimated_retry_value_is_non_negative'
|
|
996
|
-
: 'estimated_retry_value_is_negative',
|
|
997
|
-
signals: Array.from(new Set(signals)).slice(0, 16)
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
export function decideResolveIOAIManagerAutonomyPolicy(
|
|
1002
|
-
input: ResolveIOAIManagerAutonomyPolicyInput = {}
|
|
1003
|
-
): ResolveIOAIManagerAutonomyPolicyDecision {
|
|
1004
|
-
const mode = normalizeAutonomyMode(input.mode);
|
|
1005
|
-
const currentSpendUsd = nonNegativeMoney(input.currentSpendUsd);
|
|
1006
|
-
const projectedActionCostUsd = nonNegativeMoney(input.projectedActionCostUsd);
|
|
1007
|
-
const projectedSpendUsd = currentSpendUsd + projectedActionCostUsd;
|
|
1008
|
-
const budgetSoftUsd = nonNegativeMoney(input.budgetSoftUsd);
|
|
1009
|
-
const budgetHardUsd = nonNegativeMoney(input.budgetHardUsd);
|
|
1010
|
-
const softBudgetExceeded = budgetSoftUsd > 0 && projectedSpendUsd >= budgetSoftUsd;
|
|
1011
|
-
const hardBudgetExceeded = budgetHardUsd > 0 && projectedSpendUsd > budgetHardUsd;
|
|
1012
|
-
const dispatchAction = cleanText(input.dispatchAction, 120);
|
|
1013
|
-
const evidenceStrength = normalizeManagerEvidenceStrength(input.evidenceStrength);
|
|
1014
|
-
const evidenceSignals = cleanList(input.evidenceSignals, 12, 120);
|
|
1015
|
-
const materialEvidence = input.materialEvidence === true || evidenceStrength === 'material' || evidenceStrength === 'proof';
|
|
1016
|
-
const newEvidence = input.newEvidence === true;
|
|
1017
|
-
const evidenceBacked = materialEvidence || newEvidence || evidenceSignals.some((signal) => /material|proof|business|compile|infra|release|diagnosis|journey|workflow|artifact|hash_changed|fingerprint_changed/i.test(signal));
|
|
1018
|
-
const cheapEvidenceAction = isCheapEvidenceAutonomyAction(dispatchAction);
|
|
1019
|
-
const expectedValueEstimate = estimateResolveIOAIManagerRetryExpectedValue({
|
|
1020
|
-
dispatchAction,
|
|
1021
|
-
projectedActionCostUsd,
|
|
1022
|
-
currentSpendUsd,
|
|
1023
|
-
budgetHardUsd,
|
|
1024
|
-
evidenceStrength,
|
|
1025
|
-
materialEvidence,
|
|
1026
|
-
newEvidence,
|
|
1027
|
-
evidenceSignals,
|
|
1028
|
-
sameEvidenceAlreadyAttempted: input.sameEvidenceAlreadyAttempted === true
|
|
1029
|
-
});
|
|
1030
|
-
const providedExpectedValue = finiteNumber(input.expectedValueScore);
|
|
1031
|
-
const expectedValueKnown = providedExpectedValue !== undefined;
|
|
1032
|
-
const expectedValueScore = expectedValueKnown ? providedExpectedValue as number : expectedValueEstimate.score;
|
|
1033
|
-
const minExpectedValue = finiteNumber(input.minExpectedValueScore) ?? expectedValueEstimate.minScore;
|
|
1034
|
-
const expectedValuePositive = expectedValueScore >= minExpectedValue;
|
|
1035
|
-
const expectedValueSource: ResolveIOAIManagerAutonomyPolicyDecision['expectedValueSource'] = expectedValueKnown ? 'provided' : 'estimated';
|
|
1036
|
-
const requiredEvidence = [
|
|
1037
|
-
'mode',
|
|
1038
|
-
'projectedActionCostUsd',
|
|
1039
|
-
...(budgetHardUsd > 0 ? ['budgetHardUsd'] : []),
|
|
1040
|
-
...(budgetSoftUsd > 0 ? ['budgetSoftUsd'] : []),
|
|
1041
|
-
...(dispatchAction ? ['dispatchAction'] : []),
|
|
1042
|
-
...(cheapEvidenceAction ? [] : ['materialEvidence or newEvidence', 'expectedValueScore when available'])
|
|
1043
|
-
];
|
|
1044
|
-
const forbiddenActions: string[] = [];
|
|
1045
|
-
let canAutoDispatch = false;
|
|
1046
|
-
let canManualDispatch = true;
|
|
1047
|
-
let blocked = false;
|
|
1048
|
-
let requiresHumanApproval = true;
|
|
1049
|
-
let reason = '';
|
|
1050
|
-
let recommendedControl: ResolveIOAIManagerAutonomyPolicyDecision['recommendedControl'] = mode;
|
|
1051
|
-
if (mode === 'monitor_only') {
|
|
1052
|
-
canManualDispatch = false;
|
|
1053
|
-
reason = 'monitor_only_blocks_runner_dispatch';
|
|
1054
|
-
forbiddenActions.push('Do not run recovery, repair, QA, publish, deploy, or customer reply actions while in Monitor Only mode.');
|
|
1055
|
-
}
|
|
1056
|
-
else if (mode === 'manual_only') {
|
|
1057
|
-
reason = 'manual_only_requires_operator_dispatch';
|
|
1058
|
-
forbiddenActions.push('Do not auto-dispatch manager recovery actions in Manual Only mode.');
|
|
1059
|
-
}
|
|
1060
|
-
else if (hardBudgetExceeded) {
|
|
1061
|
-
blocked = true;
|
|
1062
|
-
canAutoDispatch = false;
|
|
1063
|
-
canManualDispatch = input.operatorApproved === true;
|
|
1064
|
-
reason = 'auto_retry_budget_hard_cap_would_be_exceeded';
|
|
1065
|
-
recommendedControl = 'increase_budget_or_manual';
|
|
1066
|
-
forbiddenActions.push('Do not auto-dispatch because projected spend exceeds the hard budget.');
|
|
1067
|
-
}
|
|
1068
|
-
else if (!cheapEvidenceAction && !evidenceBacked) {
|
|
1069
|
-
canAutoDispatch = false;
|
|
1070
|
-
requiresHumanApproval = true;
|
|
1071
|
-
reason = 'auto_retry_requires_material_evidence';
|
|
1072
|
-
forbiddenActions.push('Do not auto-dispatch repair work until a deterministic artifact, changed blocker fingerprint, or material proof is recorded.');
|
|
1073
|
-
}
|
|
1074
|
-
else if (!cheapEvidenceAction && !expectedValuePositive) {
|
|
1075
|
-
canAutoDispatch = false;
|
|
1076
|
-
requiresHumanApproval = true;
|
|
1077
|
-
reason = 'auto_retry_expected_value_not_positive';
|
|
1078
|
-
forbiddenActions.push('Do not auto-dispatch repair work when the expected value score is below the configured minimum.');
|
|
1079
|
-
}
|
|
1080
|
-
else {
|
|
1081
|
-
canAutoDispatch = true;
|
|
1082
|
-
requiresHumanApproval = softBudgetExceeded;
|
|
1083
|
-
reason = softBudgetExceeded
|
|
1084
|
-
? 'auto_retry_soft_budget_warning_requires_review'
|
|
1085
|
-
: 'auto_retry_within_budget_allows_auto_dispatch';
|
|
1086
|
-
if (softBudgetExceeded) {
|
|
1087
|
-
forbiddenActions.push('Do not escalate to a more expensive model or broader repair without operator review after soft budget warning.');
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
return {
|
|
1091
|
-
mode,
|
|
1092
|
-
canAutoDispatch,
|
|
1093
|
-
canManualDispatch,
|
|
1094
|
-
blocked,
|
|
1095
|
-
requiresHumanApproval,
|
|
1096
|
-
reason,
|
|
1097
|
-
currentSpendUsd,
|
|
1098
|
-
projectedActionCostUsd,
|
|
1099
|
-
projectedSpendUsd,
|
|
1100
|
-
budgetSoftUsd,
|
|
1101
|
-
budgetHardUsd,
|
|
1102
|
-
softBudgetExceeded,
|
|
1103
|
-
hardBudgetExceeded,
|
|
1104
|
-
dispatchAction,
|
|
1105
|
-
expectedValueScore,
|
|
1106
|
-
minExpectedValueScore: minExpectedValue,
|
|
1107
|
-
expectedValueKnown,
|
|
1108
|
-
expectedValueSource,
|
|
1109
|
-
expectedValuePositive,
|
|
1110
|
-
evidenceStrength,
|
|
1111
|
-
materialEvidence,
|
|
1112
|
-
newEvidence,
|
|
1113
|
-
evidenceBacked,
|
|
1114
|
-
evidenceSignals,
|
|
1115
|
-
recommendedControl,
|
|
1116
|
-
forbiddenActions,
|
|
1117
|
-
requiredEvidence
|
|
1118
|
-
};
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
function cleanDateString(value: any): string {
|
|
1122
|
-
const date = value instanceof Date ? value : new Date(value || Date.now());
|
|
1123
|
-
return Number.isNaN(date.getTime()) ? new Date().toISOString() : date.toISOString();
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
function normalizeHotfixChannel(value: any): ResolveIOAIManagerHotfixChannel | '' {
|
|
1127
|
-
const normalized = cleanText(value, 120).toLowerCase().replace(/[\s-]+/g, '_') as ResolveIOAIManagerHotfixChannel;
|
|
1128
|
-
return HOTFIX_CHANNELS.has(normalized) ? normalized : '';
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
function normalizeHotfixStatus(value: any): ResolveIOAIManagerHotfixEvidenceStatus {
|
|
1132
|
-
const normalized = cleanText(value, 120).toLowerCase().replace(/[\s-]+/g, '_');
|
|
1133
|
-
if (/^(pass|passed|success|succeeded|complete|completed|accepted)$/.test(normalized)) {
|
|
1134
|
-
return 'passed';
|
|
1135
|
-
}
|
|
1136
|
-
if (/^(block|blocked|failed|fail|error|denied)$/.test(normalized)) {
|
|
1137
|
-
return 'blocked';
|
|
1138
|
-
}
|
|
1139
|
-
if (/^(missing|none)$/.test(normalized)) {
|
|
1140
|
-
return 'missing';
|
|
1141
|
-
}
|
|
1142
|
-
return 'incomplete';
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
function hotfixStatusPassed(value: any): boolean {
|
|
1146
|
-
return normalizeHotfixStatus(value) === 'passed';
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
function normalizeHotfixEvidenceTarget(value: any): ResolveIOAIManagerHotfixEvidenceTarget {
|
|
1150
|
-
const source = cleanObject(value);
|
|
1151
|
-
return {
|
|
1152
|
-
surface: cleanText(source.surface, 160),
|
|
1153
|
-
host: cleanText(source.host || source.hostname || source.domain, 240),
|
|
1154
|
-
path: cleanText(source.path || source.remotePath || source.remote_path, 500),
|
|
1155
|
-
artifactPath: cleanText(source.artifactPath || source.artifact_path || source.localPath || source.local_path, 500),
|
|
1156
|
-
bucket: cleanText(source.bucket || source.s3Bucket || source.s3_bucket, 240),
|
|
1157
|
-
distributionId: cleanText(source.distributionId || source.distribution_id || source.cloudfrontDistributionId || source.cloudfront_distribution_id, 240),
|
|
1158
|
-
processName: cleanText(source.processName || source.process_name || source.service || source.serviceName, 160),
|
|
1159
|
-
configKey: cleanText(source.configKey || source.config_key || source.key, 240),
|
|
1160
|
-
cacheKey: cleanText(source.cacheKey || source.cache_key, 240),
|
|
1161
|
-
seedKey: cleanText(source.seedKey || source.seed_key || source.fixture, 240)
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
export function normalizeResolveIOAIManagerHotfixEvidence(
|
|
1166
|
-
value: any,
|
|
1167
|
-
policy?: ResolveIOAIManagerHotfixFirstReleasePolicy
|
|
1168
|
-
): ResolveIOAIManagerHotfixEvidence | undefined {
|
|
1169
|
-
const source = cleanObject(value);
|
|
1170
|
-
if (!Object.keys(source).length) {
|
|
1171
|
-
return undefined;
|
|
1172
|
-
}
|
|
1173
|
-
const channel = normalizeHotfixChannel(source.channel || source.hotfixChannel || source.hotfix_channel || policy?.hotfixPlan?.recommendedChannel);
|
|
1174
|
-
if (!channel) {
|
|
1175
|
-
return undefined;
|
|
1176
|
-
}
|
|
1177
|
-
const target = normalizeHotfixEvidenceTarget(source.target || source);
|
|
1178
|
-
return {
|
|
1179
|
-
channel,
|
|
1180
|
-
status: normalizeHotfixStatus(source.status),
|
|
1181
|
-
target,
|
|
1182
|
-
compiledArtifactPath: cleanText(source.compiledArtifactPath || source.compiled_artifact_path || target.artifactPath, 500),
|
|
1183
|
-
builtDistPath: cleanText(source.builtDistPath || source.built_dist_path || target.artifactPath, 500),
|
|
1184
|
-
remoteChecksumBefore: cleanText(source.remoteChecksumBefore || source.remote_checksum_before || source.checksumBefore || source.checksum_before, 160),
|
|
1185
|
-
remoteChecksumAfter: cleanText(source.remoteChecksumAfter || source.remote_checksum_after || source.checksumAfter || source.checksum_after, 160),
|
|
1186
|
-
remoteChecksum: cleanText(source.remoteChecksum || source.remote_checksum || source.checksum, 160),
|
|
1187
|
-
restartEvidence: cleanText(source.restartEvidence || source.restart_evidence, 1000),
|
|
1188
|
-
healthCheckStatus: cleanText(source.healthCheckStatus || source.health_check_status || source.health, 160),
|
|
1189
|
-
selfTestStatus: cleanText(source.selfTestStatus || source.self_test_status || source.selfTest || source.self_test, 160),
|
|
1190
|
-
releaseGateStatus: cleanText(source.releaseGateStatus || source.release_gate_status || source.releaseGate || source.release_gate, 160),
|
|
1191
|
-
s3UploadResult: cleanText(source.s3UploadResult || source.s3_upload_result || source.uploadResult || source.upload_result, 1000),
|
|
1192
|
-
cloudfrontInvalidationId: cleanText(source.cloudfrontInvalidationId || source.cloudfront_invalidation_id || source.invalidationId || source.invalidation_id, 240),
|
|
1193
|
-
publicProof: cleanText(source.publicProof || source.public_proof || source.publicUrl || source.public_url, 1000),
|
|
1194
|
-
configChangeEvidence: cleanText(source.configChangeEvidence || source.config_change_evidence, 1000),
|
|
1195
|
-
seedDataEvidence: cleanText(source.seedDataEvidence || source.seed_data_evidence, 1000),
|
|
1196
|
-
cacheInvalidationEvidence: cleanText(source.cacheInvalidationEvidence || source.cache_invalidation_evidence, 1000),
|
|
1197
|
-
serviceRestartEvidence: cleanText(source.serviceRestartEvidence || source.service_restart_evidence, 1000),
|
|
1198
|
-
releaseArtifactFingerprint: cleanText(source.releaseArtifactFingerprint || source.release_artifact_fingerprint || source.artifactFingerprint || source.artifact_fingerprint, 240),
|
|
1199
|
-
lastReleaseArtifactFingerprint: cleanText(source.lastReleaseArtifactFingerprint || source.last_release_artifact_fingerprint || source.lastArtifactFingerprint || source.last_artifact_fingerprint, 240),
|
|
1200
|
-
sourceCommitSha: cleanText(source.sourceCommitSha || source.source_commit_sha || source.gitCommitSha || source.git_commit_sha || source.commitSha || source.commit_sha, 120),
|
|
1201
|
-
githubCommitUrl: cleanText(source.githubCommitUrl || source.github_commit_url || source.gitCommitUrl || source.git_commit_url || source.commitUrl || source.commit_url, 500),
|
|
1202
|
-
gitCommitStatus: cleanText(source.gitCommitStatus || source.git_commit_status || source.githubCommitStatus || source.github_commit_status, 160),
|
|
1203
|
-
gitPushStatus: cleanText(source.gitPushStatus || source.git_push_status || source.githubPushStatus || source.github_push_status || source.githubCommitReachableStatus || source.github_commit_reachable_status, 160),
|
|
1204
|
-
committedAt: source.committedAt || source.committed_at,
|
|
1205
|
-
forceDeployReason: cleanText(source.forceDeployReason || source.force_deploy_reason, 1000),
|
|
1206
|
-
hotfixCannotResolveReason: cleanText(source.hotfixCannotResolveReason || source.hotfix_cannot_resolve_reason, 1000),
|
|
1207
|
-
fullDeployRequested: source.fullDeployRequested === true || source.full_deploy_requested === true,
|
|
1208
|
-
recordedAt: source.recordedAt || source.recorded_at
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
function matchingHotfixPlanStep(
|
|
1213
|
-
policy: ResolveIOAIManagerHotfixFirstReleasePolicy | undefined,
|
|
1214
|
-
channel: ResolveIOAIManagerHotfixChannel | ''
|
|
1215
|
-
): ResolveIOAIManagerHotfixPlanStep | undefined {
|
|
1216
|
-
return (policy?.hotfixPlan?.steps || []).find((step) => step.channel === channel);
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
function pushMissing(blockers: string[], condition: boolean, message: string): void {
|
|
1220
|
-
if (!condition) {
|
|
1221
|
-
blockers.push(message);
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
function hotfixRequiresGithubCommit(channel: ResolveIOAIManagerHotfixChannel | ''): boolean {
|
|
1226
|
-
return /^(backend_js|static_ui|config|seed_data|release_artifact|new_artifact_deploy|artifact_build)$/.test(channel);
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
function extractGithubCommitSha(value: string): string {
|
|
1230
|
-
const match = cleanText(value, 500).match(/^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/commit\/([a-f0-9]{40})(?:$|[?#/])/i);
|
|
1231
|
-
return match?.[1]?.toLowerCase() || '';
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
function isFullGitCommitSha(value: string): boolean {
|
|
1235
|
-
return /^[a-f0-9]{40}$/i.test(cleanText(value, 120));
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
function isStrongRemoteChecksum(value: string): boolean {
|
|
1239
|
-
return /^[a-f0-9]{12,128}$/i.test(cleanText(value, 160));
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
function githubCommitProofBlockers(value: ResolveIOAIManagerHotfixEvidence, channel: ResolveIOAIManagerHotfixChannel | ''): string[] {
|
|
1243
|
-
const blockers: string[] = [];
|
|
1244
|
-
const sourceCommitSha = cleanText(value.sourceCommitSha, 120);
|
|
1245
|
-
const githubCommitUrl = cleanText(value.githubCommitUrl, 500);
|
|
1246
|
-
const gitCommitStatus = cleanText(value.gitCommitStatus, 160);
|
|
1247
|
-
const gitPushStatus = cleanText(value.gitPushStatus, 160);
|
|
1248
|
-
const urlCommitSha = extractGithubCommitSha(githubCommitUrl);
|
|
1249
|
-
if (!sourceCommitSha || !githubCommitUrl) {
|
|
1250
|
-
blockers.push(`${channel} hotfix requires sourceCommitSha, githubCommitUrl, and passed gitPushStatus so live hotfixes are backed by a GitHub commit pushed before continuation.`);
|
|
1251
|
-
return blockers;
|
|
1252
|
-
}
|
|
1253
|
-
if (!isFullGitCommitSha(sourceCommitSha)) {
|
|
1254
|
-
blockers.push(`${channel} hotfix sourceCommitSha must be a full 40-character git SHA.`);
|
|
1255
|
-
}
|
|
1256
|
-
if (!urlCommitSha) {
|
|
1257
|
-
blockers.push(`${channel} hotfix githubCommitUrl must be a GitHub commit URL ending in a full 40-character git SHA.`);
|
|
1258
|
-
}
|
|
1259
|
-
else if (isFullGitCommitSha(sourceCommitSha) && urlCommitSha !== sourceCommitSha.toLowerCase()) {
|
|
1260
|
-
blockers.push(`${channel} hotfix githubCommitUrl commit SHA must match sourceCommitSha.`);
|
|
1261
|
-
}
|
|
1262
|
-
if (!gitPushStatus) {
|
|
1263
|
-
blockers.push(`${channel} hotfix requires passed gitPushStatus so the manager knows the hotfix commit is present on GitHub before continuation.`);
|
|
1264
|
-
}
|
|
1265
|
-
else if (!hotfixStatusPassed(gitPushStatus)) {
|
|
1266
|
-
blockers.push(`${channel} hotfix gitPushStatus must be passed before continuation.`);
|
|
1267
|
-
}
|
|
1268
|
-
if (gitCommitStatus && !hotfixStatusPassed(gitCommitStatus)) {
|
|
1269
|
-
blockers.push(`${channel} hotfix gitCommitStatus must be passed when provided.`);
|
|
1270
|
-
}
|
|
1271
|
-
return blockers;
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
export function evaluateResolveIOAIManagerHotfixGitProof(
|
|
1275
|
-
value: any,
|
|
1276
|
-
channelHint: ResolveIOAIManagerHotfixChannel | '' = ''
|
|
1277
|
-
): ResolveIOAIManagerHotfixGitProof {
|
|
1278
|
-
const normalized = normalizeResolveIOAIManagerHotfixEvidence(value);
|
|
1279
|
-
const source = (normalized || cleanObject(value)) as Record<string, any>;
|
|
1280
|
-
const channel = (channelHint || normalized?.channel || source.channel || '') as ResolveIOAIManagerHotfixChannel | '';
|
|
1281
|
-
const required = hotfixRequiresGithubCommit(channel);
|
|
1282
|
-
const sourceCommitSha = cleanText(source.sourceCommitSha || source.source_commit_sha || source.gitCommitSha || source.git_commit_sha || source.commitSha || source.commit_sha, 120);
|
|
1283
|
-
const githubCommitUrl = cleanText(source.githubCommitUrl || source.github_commit_url || source.gitCommitUrl || source.git_commit_url || source.commitUrl || source.commit_url, 500);
|
|
1284
|
-
const githubCommitSha = extractGithubCommitSha(githubCommitUrl);
|
|
1285
|
-
const gitCommitStatus = cleanText(source.gitCommitStatus || source.git_commit_status || source.githubCommitStatus || source.github_commit_status, 160);
|
|
1286
|
-
const gitPushStatus = cleanText(source.gitPushStatus || source.git_push_status || source.githubPushStatus || source.github_push_status || source.githubCommitReachableStatus || source.github_commit_reachable_status, 160);
|
|
1287
|
-
const blockers = required
|
|
1288
|
-
? githubCommitProofBlockers({
|
|
1289
|
-
channel: channel as ResolveIOAIManagerHotfixChannel,
|
|
1290
|
-
target: normalized?.target || {},
|
|
1291
|
-
sourceCommitSha,
|
|
1292
|
-
githubCommitUrl,
|
|
1293
|
-
gitCommitStatus,
|
|
1294
|
-
gitPushStatus
|
|
1295
|
-
}, channel)
|
|
1296
|
-
: [];
|
|
1297
|
-
const status: ResolveIOAIManagerHotfixGitProofStatus = !required
|
|
1298
|
-
? 'not_required'
|
|
1299
|
-
: (!sourceCommitSha || !githubCommitUrl || !gitPushStatus)
|
|
1300
|
-
? 'missing'
|
|
1301
|
-
: blockers.length
|
|
1302
|
-
? 'invalid'
|
|
1303
|
-
: 'passed';
|
|
1304
|
-
const passed = !required || status === 'passed';
|
|
1305
|
-
return {
|
|
1306
|
-
required,
|
|
1307
|
-
status,
|
|
1308
|
-
passed,
|
|
1309
|
-
managerMustCommitBeforeHotfix: required && !passed,
|
|
1310
|
-
channel,
|
|
1311
|
-
sourceCommitSha,
|
|
1312
|
-
githubCommitUrl,
|
|
1313
|
-
githubCommitSha,
|
|
1314
|
-
gitCommitStatus,
|
|
1315
|
-
gitPushStatus,
|
|
1316
|
-
blockers,
|
|
1317
|
-
nextCommands: required && !passed
|
|
1318
|
-
? ['commit_and_push_hotfix_to_github', 'verify_github_commit_url_and_push_status', 'record_hotfix_evidence']
|
|
1319
|
-
: [],
|
|
1320
|
-
requiredEvidence: required
|
|
1321
|
-
? ['full 40-character sourceCommitSha', 'matching GitHub commit URL', 'passed gitPushStatus']
|
|
1322
|
-
: []
|
|
1323
|
-
};
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
export function validateResolveIOAIManagerHotfixEvidence(
|
|
1327
|
-
value: any,
|
|
1328
|
-
options: { policy?: ResolveIOAIManagerHotfixFirstReleasePolicy } = {}
|
|
1329
|
-
): ResolveIOAIManagerHotfixEvidenceValidation {
|
|
1330
|
-
const normalized = normalizeResolveIOAIManagerHotfixEvidence(value, options.policy);
|
|
1331
|
-
const policy = options.policy;
|
|
1332
|
-
if (!normalized) {
|
|
1333
|
-
return {
|
|
1334
|
-
valid: false,
|
|
1335
|
-
status: 'missing',
|
|
1336
|
-
channel: '',
|
|
1337
|
-
blockers: ['Hotfix evidence is missing or channel is unsupported.'],
|
|
1338
|
-
warnings: [],
|
|
1339
|
-
fullDeployAllowed: false,
|
|
1340
|
-
fullDeployBlocked: true,
|
|
1341
|
-
hotfixSatisfied: false,
|
|
1342
|
-
requiredEvidence: policy?.hotfixPlan?.steps?.flatMap((step) => step.requiredEvidence) || [],
|
|
1343
|
-
successEvidence: policy?.hotfixPlan?.steps?.flatMap((step) => step.successEvidence) || [],
|
|
1344
|
-
githubCommitGuard: evaluateResolveIOAIManagerHotfixGitProof(value),
|
|
1345
|
-
policy,
|
|
1346
|
-
nextAction: 'record_hotfix_evidence'
|
|
1347
|
-
};
|
|
1348
|
-
}
|
|
1349
|
-
const blockers: string[] = [];
|
|
1350
|
-
const warnings: string[] = [];
|
|
1351
|
-
const target = normalized.target || {};
|
|
1352
|
-
const channel = normalized.channel;
|
|
1353
|
-
const step = matchingHotfixPlanStep(policy, channel);
|
|
1354
|
-
const requiredEvidence = step?.requiredEvidence || [];
|
|
1355
|
-
const successEvidence = step?.successEvidence || [];
|
|
1356
|
-
const policyAllowsFullDeploy = policy?.fullDeployAllowed === true;
|
|
1357
|
-
const policyBlocksDuplicate = policy?.duplicateDeployBlocked === true || policy?.duplicatePublishBlocked === true;
|
|
1358
|
-
const githubCommitGuard = evaluateResolveIOAIManagerHotfixGitProof(normalized, channel);
|
|
1359
|
-
if (normalized.fullDeployRequested && !policyAllowsFullDeploy && channel !== 'force_deploy_review' && channel !== 'new_artifact_deploy') {
|
|
1360
|
-
blockers.push('Full deploy requested but hotfix-first policy does not allow a full deploy for this release state.');
|
|
1361
|
-
}
|
|
1362
|
-
if (normalized.fullDeployRequested && policyBlocksDuplicate && !normalized.forceDeployReason && channel !== 'force_deploy_review') {
|
|
1363
|
-
blockers.push('Duplicate deploy/publish fingerprint requires force deploy/publish evidence before any full deploy.');
|
|
1364
|
-
}
|
|
1365
|
-
if (githubCommitGuard.required) {
|
|
1366
|
-
blockers.push(...githubCommitGuard.blockers);
|
|
1367
|
-
}
|
|
1368
|
-
if (channel === 'backend_js') {
|
|
1369
|
-
pushMissing(blockers, !!normalized.compiledArtifactPath, 'Backend JS hotfix requires compiledArtifactPath.');
|
|
1370
|
-
pushMissing(blockers, !!target.host, 'Backend JS hotfix requires target.host.');
|
|
1371
|
-
pushMissing(blockers, !!target.path, 'Backend JS hotfix requires target.path.');
|
|
1372
|
-
pushMissing(blockers, !!(normalized.remoteChecksumAfter || normalized.remoteChecksum), 'Backend JS hotfix requires remote checksum after replacement.');
|
|
1373
|
-
if ((normalized.remoteChecksumAfter || normalized.remoteChecksum) && !isStrongRemoteChecksum(normalized.remoteChecksumAfter || normalized.remoteChecksum || '')) {
|
|
1374
|
-
blockers.push('Backend JS hotfix remote checksum after replacement must be a strong hex checksum, not a label or placeholder.');
|
|
1375
|
-
}
|
|
1376
|
-
pushMissing(blockers, !!(normalized.restartEvidence || normalized.serviceRestartEvidence), 'Backend JS hotfix requires restart evidence.');
|
|
1377
|
-
pushMissing(blockers, hotfixStatusPassed(normalized.healthCheckStatus), 'Backend JS hotfix requires passed healthCheckStatus.');
|
|
1378
|
-
pushMissing(blockers, hotfixStatusPassed(normalized.selfTestStatus), 'Backend JS hotfix requires passed selfTestStatus.');
|
|
1379
|
-
if (normalized.remoteChecksumBefore && normalized.remoteChecksumAfter && normalized.remoteChecksumBefore === normalized.remoteChecksumAfter) {
|
|
1380
|
-
blockers.push('Backend JS hotfix checksum did not change; record force/no-op evidence or do not claim a hotfix.');
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
else if (channel === 'static_ui') {
|
|
1384
|
-
pushMissing(blockers, !!normalized.builtDistPath, 'Static UI hotfix requires builtDistPath.');
|
|
1385
|
-
pushMissing(blockers, !!target.bucket, 'Static UI hotfix requires target.bucket.');
|
|
1386
|
-
pushMissing(blockers, !!target.distributionId, 'Static UI hotfix requires target.distributionId.');
|
|
1387
|
-
pushMissing(blockers, !!normalized.s3UploadResult, 'Static UI hotfix requires s3UploadResult.');
|
|
1388
|
-
pushMissing(blockers, !!normalized.cloudfrontInvalidationId, 'Static UI hotfix requires cloudfrontInvalidationId.');
|
|
1389
|
-
pushMissing(blockers, !!normalized.publicProof, 'Static UI hotfix requires publicProof.');
|
|
1390
|
-
}
|
|
1391
|
-
else if (channel === 'config') {
|
|
1392
|
-
pushMissing(blockers, !!target.configKey, 'Config hotfix requires target.configKey.');
|
|
1393
|
-
pushMissing(blockers, !!normalized.configChangeEvidence, 'Config hotfix requires configChangeEvidence.');
|
|
1394
|
-
pushMissing(blockers, hotfixStatusPassed(normalized.releaseGateStatus), 'Config hotfix requires passed releaseGateStatus.');
|
|
1395
|
-
}
|
|
1396
|
-
else if (channel === 'seed_data') {
|
|
1397
|
-
pushMissing(blockers, !!target.seedKey, 'Seed-data hotfix requires target.seedKey.');
|
|
1398
|
-
pushMissing(blockers, !!normalized.seedDataEvidence, 'Seed-data hotfix requires seedDataEvidence.');
|
|
1399
|
-
pushMissing(blockers, hotfixStatusPassed(normalized.releaseGateStatus), 'Seed-data hotfix requires passed releaseGateStatus.');
|
|
1400
|
-
}
|
|
1401
|
-
else if (channel === 'cache_invalidation') {
|
|
1402
|
-
pushMissing(blockers, !!target.cacheKey, 'Cache invalidation hotfix requires target.cacheKey.');
|
|
1403
|
-
pushMissing(blockers, !!normalized.cacheInvalidationEvidence, 'Cache invalidation hotfix requires cacheInvalidationEvidence.');
|
|
1404
|
-
pushMissing(blockers, hotfixStatusPassed(normalized.releaseGateStatus) || hotfixStatusPassed(normalized.healthCheckStatus), 'Cache invalidation hotfix requires passed releaseGateStatus or healthCheckStatus.');
|
|
1405
|
-
}
|
|
1406
|
-
else if (channel === 'service_restart') {
|
|
1407
|
-
pushMissing(blockers, !!target.processName, 'Service restart hotfix requires target.processName.');
|
|
1408
|
-
pushMissing(blockers, !!(normalized.serviceRestartEvidence || normalized.restartEvidence), 'Service restart hotfix requires restart evidence.');
|
|
1409
|
-
pushMissing(blockers, hotfixStatusPassed(normalized.healthCheckStatus), 'Service restart hotfix requires passed healthCheckStatus.');
|
|
1410
|
-
pushMissing(blockers, hotfixStatusPassed(normalized.selfTestStatus), 'Service restart hotfix requires passed selfTestStatus.');
|
|
1411
|
-
}
|
|
1412
|
-
else if (channel === 'release_artifact') {
|
|
1413
|
-
pushMissing(blockers, !!normalized.releaseArtifactFingerprint, 'Release artifact hotfix requires releaseArtifactFingerprint.');
|
|
1414
|
-
pushMissing(blockers, hotfixStatusPassed(normalized.releaseGateStatus), 'Release artifact hotfix requires passed releaseGateStatus.');
|
|
1415
|
-
}
|
|
1416
|
-
else if (channel === 'force_deploy_review') {
|
|
1417
|
-
pushMissing(blockers, !!normalized.forceDeployReason, 'Force deploy review requires forceDeployReason.');
|
|
1418
|
-
pushMissing(blockers, !!normalized.hotfixCannotResolveReason, 'Force deploy review requires hotfixCannotResolveReason.');
|
|
1419
|
-
}
|
|
1420
|
-
else if (channel === 'new_artifact_deploy') {
|
|
1421
|
-
pushMissing(blockers, !!normalized.releaseArtifactFingerprint, 'New artifact deploy requires releaseArtifactFingerprint.');
|
|
1422
|
-
pushMissing(blockers, !!normalized.lastReleaseArtifactFingerprint, 'New artifact deploy requires lastReleaseArtifactFingerprint.');
|
|
1423
|
-
if (normalized.releaseArtifactFingerprint && normalized.lastReleaseArtifactFingerprint && normalized.releaseArtifactFingerprint === normalized.lastReleaseArtifactFingerprint) {
|
|
1424
|
-
blockers.push('New artifact deploy requires a fingerprint different from the last deployed artifact.');
|
|
1425
|
-
}
|
|
1426
|
-
pushMissing(blockers, hotfixStatusPassed(normalized.releaseGateStatus), 'New artifact deploy requires passed releaseGateStatus.');
|
|
1427
|
-
}
|
|
1428
|
-
else if (channel === 'artifact_build') {
|
|
1429
|
-
pushMissing(blockers, !!normalized.releaseArtifactFingerprint, 'Artifact build requires releaseArtifactFingerprint.');
|
|
1430
|
-
if (normalized.fullDeployRequested) {
|
|
1431
|
-
blockers.push('Artifact build evidence cannot request a full deploy in the same step.');
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
|
-
if (!step && policy) {
|
|
1435
|
-
warnings.push(`Hotfix channel ${channel} is not the recommended channel in the current release policy.`);
|
|
1436
|
-
}
|
|
1437
|
-
const forceDeployReviewValid = channel === 'force_deploy_review' && blockers.length === 0;
|
|
1438
|
-
const newArtifactDeployValid = channel === 'new_artifact_deploy' && blockers.length === 0;
|
|
1439
|
-
const fullDeployAllowed = forceDeployReviewValid || newArtifactDeployValid || (policyAllowsFullDeploy && blockers.length === 0 && normalized.fullDeployRequested === true);
|
|
1440
|
-
const hotfixSatisfied = blockers.length === 0 && !fullDeployAllowed;
|
|
1441
|
-
const status: ResolveIOAIManagerHotfixEvidenceStatus = blockers.length
|
|
1442
|
-
? (normalizeHotfixStatus(normalized.status) === 'blocked' ? 'blocked' : 'incomplete')
|
|
1443
|
-
: 'passed';
|
|
1444
|
-
const fullDeployBlocked = !fullDeployAllowed && (normalized.fullDeployRequested === true || policy?.fullDeployAllowed !== true);
|
|
1445
|
-
const nextAction: ResolveIOAIManagerHotfixEvidenceValidation['nextAction'] = blockers.some((blocker) => /force deploy/i.test(blocker))
|
|
1446
|
-
? 'request_force_deploy_reason'
|
|
1447
|
-
: fullDeployAllowed
|
|
1448
|
-
? 'allow_one_full_deploy'
|
|
1449
|
-
: hotfixSatisfied
|
|
1450
|
-
? 'rerun_release_gate'
|
|
1451
|
-
: blockers.length
|
|
1452
|
-
? 'record_hotfix_evidence'
|
|
1453
|
-
: 'park_manual';
|
|
1454
|
-
normalized.status = status;
|
|
1455
|
-
return {
|
|
1456
|
-
valid: blockers.length === 0,
|
|
1457
|
-
status,
|
|
1458
|
-
channel,
|
|
1459
|
-
blockers,
|
|
1460
|
-
warnings,
|
|
1461
|
-
fullDeployAllowed,
|
|
1462
|
-
fullDeployBlocked,
|
|
1463
|
-
hotfixSatisfied,
|
|
1464
|
-
requiredEvidence,
|
|
1465
|
-
successEvidence,
|
|
1466
|
-
normalized,
|
|
1467
|
-
githubCommitGuard,
|
|
1468
|
-
policy,
|
|
1469
|
-
nextAction
|
|
1470
|
-
};
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
function mergeHotfixEvidenceRecordInput(input: ResolveIOAIManagerHotfixEvidenceRecordInput): Record<string, any> {
|
|
1474
|
-
const merged = {
|
|
1475
|
-
...cleanObject(input.evidence)
|
|
1476
|
-
};
|
|
1477
|
-
const directFields = [
|
|
1478
|
-
'channel',
|
|
1479
|
-
'status',
|
|
1480
|
-
'target',
|
|
1481
|
-
'compiledArtifactPath',
|
|
1482
|
-
'builtDistPath',
|
|
1483
|
-
'remoteChecksumBefore',
|
|
1484
|
-
'remoteChecksumAfter',
|
|
1485
|
-
'remoteChecksum',
|
|
1486
|
-
'restartEvidence',
|
|
1487
|
-
'healthCheckStatus',
|
|
1488
|
-
'selfTestStatus',
|
|
1489
|
-
'releaseGateStatus',
|
|
1490
|
-
's3UploadResult',
|
|
1491
|
-
'cloudfrontInvalidationId',
|
|
1492
|
-
'publicProof',
|
|
1493
|
-
'configChangeEvidence',
|
|
1494
|
-
'seedDataEvidence',
|
|
1495
|
-
'cacheInvalidationEvidence',
|
|
1496
|
-
'serviceRestartEvidence',
|
|
1497
|
-
'releaseArtifactFingerprint',
|
|
1498
|
-
'lastReleaseArtifactFingerprint',
|
|
1499
|
-
'sourceCommitSha',
|
|
1500
|
-
'githubCommitUrl',
|
|
1501
|
-
'gitCommitStatus',
|
|
1502
|
-
'gitPushStatus',
|
|
1503
|
-
'committedAt',
|
|
1504
|
-
'forceDeployReason',
|
|
1505
|
-
'hotfixCannotResolveReason',
|
|
1506
|
-
'fullDeployRequested',
|
|
1507
|
-
'recordedAt'
|
|
1508
|
-
];
|
|
1509
|
-
for (const field of directFields) {
|
|
1510
|
-
const value = (input as any)[field];
|
|
1511
|
-
if (value !== undefined && value !== null && value !== '') {
|
|
1512
|
-
(merged as any)[field] = value;
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
return merged;
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
function resolveHotfixEvidenceRequiredFields(
|
|
1519
|
-
channel: ResolveIOAIManagerHotfixChannel | ''
|
|
1520
|
-
): string[] {
|
|
1521
|
-
const fields = ['channel'];
|
|
1522
|
-
if (hotfixRequiresGithubCommit(channel)) {
|
|
1523
|
-
fields.push('sourceCommitSha', 'githubCommitUrl', 'gitPushStatus');
|
|
1524
|
-
}
|
|
1525
|
-
if (channel === 'backend_js') {
|
|
1526
|
-
fields.push('compiledArtifactPath', 'target.host', 'target.path', 'remoteChecksumAfter', 'restartEvidence', 'healthCheckStatus', 'selfTestStatus');
|
|
1527
|
-
}
|
|
1528
|
-
else if (channel === 'static_ui') {
|
|
1529
|
-
fields.push('builtDistPath', 'target.bucket', 'target.distributionId', 's3UploadResult', 'cloudfrontInvalidationId', 'publicProof');
|
|
1530
|
-
}
|
|
1531
|
-
else if (channel === 'config') {
|
|
1532
|
-
fields.push('target.configKey', 'configChangeEvidence', 'releaseGateStatus');
|
|
1533
|
-
}
|
|
1534
|
-
else if (channel === 'seed_data') {
|
|
1535
|
-
fields.push('target.seedKey', 'seedDataEvidence', 'releaseGateStatus');
|
|
1536
|
-
}
|
|
1537
|
-
else if (channel === 'cache_invalidation') {
|
|
1538
|
-
fields.push('target.cacheKey', 'cacheInvalidationEvidence', 'releaseGateStatus_or_healthCheckStatus');
|
|
1539
|
-
}
|
|
1540
|
-
else if (channel === 'service_restart') {
|
|
1541
|
-
fields.push('target.processName', 'serviceRestartEvidence', 'healthCheckStatus', 'selfTestStatus');
|
|
1542
|
-
}
|
|
1543
|
-
else if (channel === 'release_artifact') {
|
|
1544
|
-
fields.push('releaseArtifactFingerprint', 'releaseGateStatus');
|
|
1545
|
-
}
|
|
1546
|
-
else if (channel === 'new_artifact_deploy') {
|
|
1547
|
-
fields.push('releaseArtifactFingerprint', 'lastReleaseArtifactFingerprint', 'releaseGateStatus');
|
|
1548
|
-
}
|
|
1549
|
-
else if (channel === 'artifact_build') {
|
|
1550
|
-
fields.push('releaseArtifactFingerprint');
|
|
1551
|
-
}
|
|
1552
|
-
else if (channel === 'force_deploy_review') {
|
|
1553
|
-
fields.push('forceDeployReason', 'hotfixCannotResolveReason');
|
|
1554
|
-
}
|
|
1555
|
-
return Array.from(new Set(fields));
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
|
-
function hotfixEvidenceFieldPresent(
|
|
1559
|
-
evidence: ResolveIOAIManagerHotfixEvidence | undefined,
|
|
1560
|
-
field: string
|
|
1561
|
-
): boolean {
|
|
1562
|
-
if (!evidence) {
|
|
1563
|
-
return false;
|
|
1564
|
-
}
|
|
1565
|
-
const target = evidence.target || {};
|
|
1566
|
-
switch (field) {
|
|
1567
|
-
case 'channel':
|
|
1568
|
-
return !!evidence.channel;
|
|
1569
|
-
case 'sourceCommitSha':
|
|
1570
|
-
return isFullGitCommitSha(evidence.sourceCommitSha || '');
|
|
1571
|
-
case 'githubCommitUrl':
|
|
1572
|
-
return !!extractGithubCommitSha(evidence.githubCommitUrl || '');
|
|
1573
|
-
case 'gitPushStatus':
|
|
1574
|
-
return hotfixStatusPassed(evidence.gitPushStatus);
|
|
1575
|
-
case 'compiledArtifactPath':
|
|
1576
|
-
return !!cleanText(evidence.compiledArtifactPath, 500);
|
|
1577
|
-
case 'builtDistPath':
|
|
1578
|
-
return !!cleanText(evidence.builtDistPath, 500);
|
|
1579
|
-
case 'remoteChecksumAfter':
|
|
1580
|
-
return !!cleanText(evidence.remoteChecksumAfter || evidence.remoteChecksum, 160);
|
|
1581
|
-
case 'restartEvidence':
|
|
1582
|
-
return !!cleanText(evidence.restartEvidence || evidence.serviceRestartEvidence, 1000);
|
|
1583
|
-
case 'serviceRestartEvidence':
|
|
1584
|
-
return !!cleanText(evidence.serviceRestartEvidence || evidence.restartEvidence, 1000);
|
|
1585
|
-
case 'healthCheckStatus':
|
|
1586
|
-
return hotfixStatusPassed(evidence.healthCheckStatus);
|
|
1587
|
-
case 'selfTestStatus':
|
|
1588
|
-
return hotfixStatusPassed(evidence.selfTestStatus);
|
|
1589
|
-
case 'releaseGateStatus':
|
|
1590
|
-
return hotfixStatusPassed(evidence.releaseGateStatus);
|
|
1591
|
-
case 'releaseGateStatus_or_healthCheckStatus':
|
|
1592
|
-
return hotfixStatusPassed(evidence.releaseGateStatus) || hotfixStatusPassed(evidence.healthCheckStatus);
|
|
1593
|
-
case 's3UploadResult':
|
|
1594
|
-
return !!cleanText(evidence.s3UploadResult, 1000);
|
|
1595
|
-
case 'cloudfrontInvalidationId':
|
|
1596
|
-
return !!cleanText(evidence.cloudfrontInvalidationId, 240);
|
|
1597
|
-
case 'publicProof':
|
|
1598
|
-
return !!cleanText(evidence.publicProof, 1000);
|
|
1599
|
-
case 'configChangeEvidence':
|
|
1600
|
-
return !!cleanText(evidence.configChangeEvidence, 1000);
|
|
1601
|
-
case 'seedDataEvidence':
|
|
1602
|
-
return !!cleanText(evidence.seedDataEvidence, 1000);
|
|
1603
|
-
case 'cacheInvalidationEvidence':
|
|
1604
|
-
return !!cleanText(evidence.cacheInvalidationEvidence, 1000);
|
|
1605
|
-
case 'releaseArtifactFingerprint':
|
|
1606
|
-
return !!cleanText(evidence.releaseArtifactFingerprint, 240);
|
|
1607
|
-
case 'lastReleaseArtifactFingerprint':
|
|
1608
|
-
return !!cleanText(evidence.lastReleaseArtifactFingerprint, 240);
|
|
1609
|
-
case 'forceDeployReason':
|
|
1610
|
-
return !!cleanText(evidence.forceDeployReason, 1000);
|
|
1611
|
-
case 'hotfixCannotResolveReason':
|
|
1612
|
-
return !!cleanText(evidence.hotfixCannotResolveReason, 1000);
|
|
1613
|
-
case 'target.host':
|
|
1614
|
-
return !!cleanText(target.host, 200);
|
|
1615
|
-
case 'target.path':
|
|
1616
|
-
return !!cleanText(target.path, 500);
|
|
1617
|
-
case 'target.bucket':
|
|
1618
|
-
return !!cleanText(target.bucket, 200);
|
|
1619
|
-
case 'target.distributionId':
|
|
1620
|
-
return !!cleanText(target.distributionId, 200);
|
|
1621
|
-
case 'target.configKey':
|
|
1622
|
-
return !!cleanText(target.configKey, 200);
|
|
1623
|
-
case 'target.seedKey':
|
|
1624
|
-
return !!cleanText(target.seedKey, 200);
|
|
1625
|
-
case 'target.cacheKey':
|
|
1626
|
-
return !!cleanText(target.cacheKey, 200);
|
|
1627
|
-
case 'target.processName':
|
|
1628
|
-
return !!cleanText(target.processName, 200);
|
|
1629
|
-
default:
|
|
1630
|
-
return false;
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
export function buildResolveIOAIManagerHotfixEvidenceRecord(
|
|
1635
|
-
input: ResolveIOAIManagerHotfixEvidenceRecordInput = {}
|
|
1636
|
-
): ResolveIOAIManagerHotfixEvidenceRecord {
|
|
1637
|
-
const merged = mergeHotfixEvidenceRecordInput(input);
|
|
1638
|
-
const normalized = normalizeResolveIOAIManagerHotfixEvidence(merged, input.policy);
|
|
1639
|
-
const validation = validateResolveIOAIManagerHotfixEvidence(merged, { policy: input.policy });
|
|
1640
|
-
const channel = validation.channel || normalized?.channel || input.policy?.hotfixPlan?.recommendedChannel || '';
|
|
1641
|
-
const requiredFields = resolveHotfixEvidenceRequiredFields(channel);
|
|
1642
|
-
const missingFields = requiredFields.filter((field) => !hotfixEvidenceFieldPresent(normalized, field));
|
|
1643
|
-
const recordedAt = cleanDateString(input.now || normalized?.recordedAt || new Date());
|
|
1644
|
-
const githubCommitGuard = validation.githubCommitGuard || evaluateResolveIOAIManagerHotfixGitProof(normalized || merged, channel);
|
|
1645
|
-
return {
|
|
1646
|
-
recordId: cleanText(input.recordId, 160) || `hotfix-${fingerprintResolveIOAIManagerBlocker(`${channel}:${githubCommitGuard.sourceCommitSha}:${recordedAt}`)}`,
|
|
1647
|
-
recordedAt,
|
|
1648
|
-
recordedBy: cleanText(input.recordedBy || 'manager', 160),
|
|
1649
|
-
source: cleanText(input.source || 'unknown', 80),
|
|
1650
|
-
reason: cleanText(input.reason, 1000),
|
|
1651
|
-
channel,
|
|
1652
|
-
status: validation.status,
|
|
1653
|
-
nextAction: validation.nextAction,
|
|
1654
|
-
valid: validation.valid,
|
|
1655
|
-
hotfixSatisfied: validation.hotfixSatisfied,
|
|
1656
|
-
fullDeployAllowed: validation.fullDeployAllowed,
|
|
1657
|
-
fullDeployBlocked: validation.fullDeployBlocked,
|
|
1658
|
-
requiredFields,
|
|
1659
|
-
missingFields,
|
|
1660
|
-
evidence: normalized,
|
|
1661
|
-
validation,
|
|
1662
|
-
sourceCommitSha: githubCommitGuard.sourceCommitSha,
|
|
1663
|
-
githubCommitUrl: githubCommitGuard.githubCommitUrl,
|
|
1664
|
-
gitPushStatus: githubCommitGuard.gitPushStatus,
|
|
1665
|
-
githubCommitRequired: githubCommitGuard.required,
|
|
1666
|
-
githubCommitProofPassed: githubCommitGuard.passed,
|
|
1667
|
-
commitProofStatus: githubCommitGuard.status,
|
|
1668
|
-
managerMustCommitBeforeHotfix: githubCommitGuard.managerMustCommitBeforeHotfix,
|
|
1669
|
-
githubCommitGuard,
|
|
1670
|
-
readyForReleaseGate: validation.valid === true && validation.nextAction === 'rerun_release_gate',
|
|
1671
|
-
readyForContinuation: validation.valid === true && validation.hotfixSatisfied === true && hotfixStatusPassed(normalized?.releaseGateStatus)
|
|
1672
|
-
};
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
export function decideResolveIOAIManagerHotfixContinuation(
|
|
1676
|
-
input: ResolveIOAIManagerHotfixContinuationInput = {}
|
|
1677
|
-
): ResolveIOAIManagerHotfixContinuationDecision {
|
|
1678
|
-
const validation = validateResolveIOAIManagerHotfixEvidence(input.evidence, { policy: input.policy });
|
|
1679
|
-
const failureClass = cleanText(input.failureClass, 120) || 'release';
|
|
1680
|
-
const blockerText = cleanText(input.blocker || validation.blockers.join('; ') || validation.normalized?.hotfixCannotResolveReason, 1000);
|
|
1681
|
-
const blockerFingerprint = fingerprintResolveIOAIManagerBlocker(blockerText || `${validation.channel}:${validation.status}:${validation.nextAction}`);
|
|
1682
|
-
const baseBlockers = [...validation.blockers];
|
|
1683
|
-
const githubCommitGuard = validation.githubCommitGuard || evaluateResolveIOAIManagerHotfixGitProof(input.evidence, validation.channel);
|
|
1684
|
-
const needsGithubCommitProof = githubCommitGuard.managerMustCommitBeforeHotfix
|
|
1685
|
-
|| validation.blockers.some((blocker) => /sourceCommitSha|githubCommitUrl|gitPushStatus|gitCommitStatus|GitHub commit|pushed/i.test(blocker));
|
|
1686
|
-
const repeatedFailure = input.repeatedFailure === true;
|
|
1687
|
-
const releaseGatePassed = input.releaseGatePassed === true
|
|
1688
|
-
|| hotfixStatusPassed(validation.normalized?.releaseGateStatus);
|
|
1689
|
-
let action: ResolveIOAIManagerHotfixContinuationAction = validation.nextAction;
|
|
1690
|
-
let canContinueRun = false;
|
|
1691
|
-
let canRunFullDeploy = validation.fullDeployAllowed;
|
|
1692
|
-
let shouldRerunReleaseGate = false;
|
|
1693
|
-
let shouldPark = false;
|
|
1694
|
-
let reason = '';
|
|
1695
|
-
const nextCommands: string[] = [];
|
|
1696
|
-
|
|
1697
|
-
if (githubCommitGuard.managerMustCommitBeforeHotfix) {
|
|
1698
|
-
action = 'record_hotfix_evidence';
|
|
1699
|
-
reason = githubCommitGuard.blockers.length
|
|
1700
|
-
? `Manager hotfix cannot continue until the hotfix is committed and pushed: ${githubCommitGuard.blockers.join('; ')}`
|
|
1701
|
-
: 'Manager hotfix cannot continue until sourceCommitSha, githubCommitUrl, and passed gitPushStatus prove the hotfix is on GitHub.';
|
|
1702
|
-
nextCommands.push(...githubCommitGuard.nextCommands, 'rerun_hotfix_evidence_validator');
|
|
1703
|
-
for (const blocker of githubCommitGuard.blockers) {
|
|
1704
|
-
if (!baseBlockers.includes(blocker)) {
|
|
1705
|
-
baseBlockers.push(blocker);
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
else if (!validation.valid || validation.status === 'missing' || validation.status === 'incomplete') {
|
|
1710
|
-
action = validation.nextAction === 'request_force_deploy_reason'
|
|
1711
|
-
? 'request_force_deploy_reason'
|
|
1712
|
-
: 'record_hotfix_evidence';
|
|
1713
|
-
reason = validation.blockers.length
|
|
1714
|
-
? `Hotfix evidence is not complete: ${validation.blockers.join('; ')}`
|
|
1715
|
-
: 'Hotfix evidence is missing; record the hotfix target, checksum, restart, health, and self-test proof before continuing.';
|
|
1716
|
-
if (needsGithubCommitProof) {
|
|
1717
|
-
nextCommands.push('commit_and_push_hotfix_to_github', 'verify_github_commit_url_and_push_status');
|
|
1718
|
-
}
|
|
1719
|
-
nextCommands.push('record_hotfix_evidence', 'rerun_hotfix_evidence_validator');
|
|
1720
|
-
}
|
|
1721
|
-
else if (validation.fullDeployAllowed) {
|
|
1722
|
-
action = 'allow_one_full_deploy';
|
|
1723
|
-
canRunFullDeploy = true;
|
|
1724
|
-
reason = 'Hotfix evidence proves the duplicate release cannot be resolved by a hotfix, or a new artifact was verified, so exactly one full deploy can proceed.';
|
|
1725
|
-
nextCommands.push('run_release_gate_once', 'execute_one_full_deploy_with_force_evidence', 'record_deploy_result');
|
|
1726
|
-
}
|
|
1727
|
-
else if (repeatedFailure) {
|
|
1728
|
-
action = 'park_manual';
|
|
1729
|
-
shouldPark = true;
|
|
1730
|
-
reason = 'The same release failure repeated after valid hotfix evidence; park for manual review instead of looping or redeploying blindly.';
|
|
1731
|
-
nextCommands.push('park_manual_with_hotfix_evidence', 'attach_release_gate_logs');
|
|
1732
|
-
}
|
|
1733
|
-
else if (validation.hotfixSatisfied && releaseGatePassed) {
|
|
1734
|
-
action = 'continue_runner';
|
|
1735
|
-
canContinueRun = true;
|
|
1736
|
-
reason = 'Hotfix evidence and the release gate both passed; continue the current runner without a full deploy.';
|
|
1737
|
-
nextCommands.push('continue_current_runner', 'record_hotfix_continuation');
|
|
1738
|
-
}
|
|
1739
|
-
else if (validation.hotfixSatisfied) {
|
|
1740
|
-
action = 'rerun_release_gate';
|
|
1741
|
-
shouldRerunReleaseGate = true;
|
|
1742
|
-
reason = 'Hotfix evidence passed; rerun the smallest release gate before continuing the runner.';
|
|
1743
|
-
nextCommands.push('rerun_release_gate_once', 'record_release_gate_status');
|
|
1744
|
-
}
|
|
1745
|
-
else {
|
|
1746
|
-
action = 'park_manual';
|
|
1747
|
-
shouldPark = true;
|
|
1748
|
-
reason = 'Hotfix evidence reached an unsupported state; park instead of starting another broad deploy or model loop.';
|
|
1749
|
-
baseBlockers.push('Unsupported hotfix continuation state.');
|
|
1750
|
-
nextCommands.push('park_manual_with_hotfix_evidence');
|
|
1751
|
-
}
|
|
1752
|
-
|
|
1753
|
-
return {
|
|
1754
|
-
action,
|
|
1755
|
-
canContinueRun,
|
|
1756
|
-
canRunFullDeploy,
|
|
1757
|
-
shouldRerunReleaseGate,
|
|
1758
|
-
shouldPark,
|
|
1759
|
-
reason,
|
|
1760
|
-
blockers: baseBlockers,
|
|
1761
|
-
warnings: validation.warnings,
|
|
1762
|
-
requiredEvidence: validation.requiredEvidence,
|
|
1763
|
-
successEvidence: validation.successEvidence,
|
|
1764
|
-
nextCommands: Array.from(new Set(nextCommands)),
|
|
1765
|
-
failureClass,
|
|
1766
|
-
blockerFingerprint,
|
|
1767
|
-
githubCommitGuard,
|
|
1768
|
-
validation,
|
|
1769
|
-
recordedAt: isoNow(input.now)
|
|
1770
|
-
};
|
|
1771
|
-
}
|
|
1772
|
-
|
|
1773
|
-
export function decideResolveIOAIManagerHotfixCommitGuard(
|
|
1774
|
-
input: ResolveIOAIManagerHotfixContinuationInput = {}
|
|
1775
|
-
): ResolveIOAIManagerHotfixCommitGuardDecision {
|
|
1776
|
-
const normalized = normalizeResolveIOAIManagerHotfixEvidence(input.evidence, input.policy);
|
|
1777
|
-
const validation = validateResolveIOAIManagerHotfixEvidence(input.evidence, { policy: input.policy });
|
|
1778
|
-
const githubCommitGuard = validation.githubCommitGuard || evaluateResolveIOAIManagerHotfixGitProof(input.evidence, validation.channel);
|
|
1779
|
-
const active = !!normalized || githubCommitGuard.required === true;
|
|
1780
|
-
const hotfixCommitBlockers = validation.blockers.filter((blocker) => /sourceCommitSha|githubCommitUrl|gitPushStatus|gitCommitStatus|GitHub commit|pushed/i.test(blocker));
|
|
1781
|
-
const blockers = Array.from(new Set([
|
|
1782
|
-
...(githubCommitGuard.blockers || []),
|
|
1783
|
-
...hotfixCommitBlockers
|
|
1784
|
-
]));
|
|
1785
|
-
const blocked = active && (
|
|
1786
|
-
githubCommitGuard.managerMustCommitBeforeHotfix === true
|
|
1787
|
-
|| (githubCommitGuard.required === true && githubCommitGuard.passed !== true)
|
|
1788
|
-
|| hotfixCommitBlockers.length > 0
|
|
1789
|
-
);
|
|
1790
|
-
const nextCommands = blocked
|
|
1791
|
-
? Array.from(new Set([
|
|
1792
|
-
...(githubCommitGuard.nextCommands || []),
|
|
1793
|
-
'commit_and_push_hotfix_to_github',
|
|
1794
|
-
'verify_github_commit_url_and_push_status',
|
|
1795
|
-
'record_hotfix_evidence'
|
|
1796
|
-
]))
|
|
1797
|
-
: [];
|
|
1798
|
-
return {
|
|
1799
|
-
active,
|
|
1800
|
-
blocked,
|
|
1801
|
-
reason: blocked
|
|
1802
|
-
? (blockers.length
|
|
1803
|
-
? `Hotfix continuation is blocked until GitHub commit proof is recorded: ${blockers.join('; ')}`
|
|
1804
|
-
: 'Hotfix continuation is blocked until sourceCommitSha, githubCommitUrl, and passed gitPushStatus prove the hotfix is pushed to GitHub.')
|
|
1805
|
-
: (active ? 'Hotfix GitHub commit proof is recorded.' : 'No hotfix commit guard is active.'),
|
|
1806
|
-
nextAction: blocked ? 'record_hotfix_evidence' : 'continue',
|
|
1807
|
-
channel: validation.channel || githubCommitGuard.channel || '',
|
|
1808
|
-
blockers,
|
|
1809
|
-
nextCommands,
|
|
1810
|
-
requiredEvidence: githubCommitGuard.requiredEvidence,
|
|
1811
|
-
githubCommitGuard,
|
|
1812
|
-
validation
|
|
1813
|
-
};
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1816
|
-
function releaseStatusIsBlocked(value: string): boolean {
|
|
1817
|
-
return /fail|error|blocked|missing|empty|not ready|not_ready|denied|invalid|timeout|stale/i.test(value);
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
function releaseStatusIsTerminalDeploy(value: string): boolean {
|
|
1821
|
-
return /requested|queued|in.?progress|completed|complete|success|succeeded|pass|passed|published|deployed|failed|error|blocked/i.test(value);
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
function buildReleaseAllowedActions(action: ResolveIOAIManagerHotfixFirstReleaseAction): string[] {
|
|
1825
|
-
if (action === 'wait_for_active_deploy') {
|
|
1826
|
-
return ['wait_for_current_deploy_result', 'collect_current_deploy_log', 'refresh_release_snapshot'];
|
|
1827
|
-
}
|
|
1828
|
-
if (action === 'block_duplicate_deploy') {
|
|
1829
|
-
return ['collect_existing_deploy_evidence', 'hotfix_release_artifact', 'request_force_deploy_with_reason'];
|
|
1830
|
-
}
|
|
1831
|
-
if (action === 'block_duplicate_publish') {
|
|
1832
|
-
return ['collect_existing_publish_evidence', 'hotfix_release_artifact', 'request_force_publish_with_reason'];
|
|
1833
|
-
}
|
|
1834
|
-
if (action === 'review_force_deploy') {
|
|
1835
|
-
return ['verify_force_deploy_reason', 'rerun_failed_release_gate_once', 'record_force_deploy_evidence'];
|
|
1836
|
-
}
|
|
1837
|
-
if (action === 'deploy_new_artifact_after_release_gate') {
|
|
1838
|
-
return ['verify_new_artifact_fingerprint', 'run_release_gate_once', 'record_publish_deploy_evidence'];
|
|
1839
|
-
}
|
|
1840
|
-
if (action === 'build_artifact_first') {
|
|
1841
|
-
return ['build_missing_release_artifact', 'record_artifact_fingerprint', 'run_release_gate_once'];
|
|
1842
|
-
}
|
|
1843
|
-
return ['hotfix_live_runner_or_release_artifact', 'rerun_failed_release_gate_only', 'record_release_evidence'];
|
|
1844
|
-
}
|
|
1845
|
-
|
|
1846
|
-
function hotfixStep(
|
|
1847
|
-
id: string,
|
|
1848
|
-
label: string,
|
|
1849
|
-
channel: ResolveIOAIManagerHotfixChannel,
|
|
1850
|
-
options: {
|
|
1851
|
-
safeToAutoRun?: boolean;
|
|
1852
|
-
requiresFullDeploy?: boolean;
|
|
1853
|
-
requiredEvidence?: string[];
|
|
1854
|
-
successEvidence?: string[];
|
|
1855
|
-
blockedBy?: string[];
|
|
1856
|
-
} = {}
|
|
1857
|
-
): ResolveIOAIManagerHotfixPlanStep {
|
|
1858
|
-
return {
|
|
1859
|
-
id,
|
|
1860
|
-
label,
|
|
1861
|
-
channel,
|
|
1862
|
-
safeToAutoRun: options.safeToAutoRun === true,
|
|
1863
|
-
requiresFullDeploy: options.requiresFullDeploy === true,
|
|
1864
|
-
requiredEvidence: options.requiredEvidence || [],
|
|
1865
|
-
successEvidence: options.successEvidence || [],
|
|
1866
|
-
blockedBy: options.blockedBy || []
|
|
1867
|
-
};
|
|
1868
|
-
}
|
|
1869
|
-
|
|
1870
|
-
function buildHotfixFirstReleasePlan(input: {
|
|
1871
|
-
action: ResolveIOAIManagerHotfixFirstReleaseAction;
|
|
1872
|
-
surface: string;
|
|
1873
|
-
reason: string;
|
|
1874
|
-
releaseBlocked: boolean;
|
|
1875
|
-
}): ResolveIOAIManagerHotfixPlan {
|
|
1876
|
-
const surface = cleanText(input.surface || 'runner', 80)
|
|
1877
|
-
.toLowerCase()
|
|
1878
|
-
.replace(/[^a-z0-9]+/g, '_')
|
|
1879
|
-
.replace(/^_+|_+$/g, '') || 'runner';
|
|
1880
|
-
const commonAcceptance = [
|
|
1881
|
-
'failed release gate is rerun once and passes',
|
|
1882
|
-
'artifact fingerprint or hotfix evidence is recorded',
|
|
1883
|
-
'operator console shows no active duplicate deploy blocker'
|
|
1884
|
-
];
|
|
1885
|
-
if (input.action === 'wait_for_active_deploy') {
|
|
1886
|
-
return {
|
|
1887
|
-
planId: `${surface}:wait_for_active_deploy`,
|
|
1888
|
-
label: 'Wait For Active Deploy',
|
|
1889
|
-
recommendedChannel: 'release_artifact',
|
|
1890
|
-
reason: input.reason,
|
|
1891
|
-
fullDeployAvoided: true,
|
|
1892
|
-
steps: [
|
|
1893
|
-
hotfixStep('collect_active_deploy_log', 'Collect active deploy log', 'release_artifact', {
|
|
1894
|
-
safeToAutoRun: true,
|
|
1895
|
-
requiredEvidence: ['active deploy id or release operation id'],
|
|
1896
|
-
successEvidence: ['terminal deploy status and release log captured']
|
|
1897
|
-
})
|
|
1898
|
-
],
|
|
1899
|
-
acceptanceEvidence: commonAcceptance,
|
|
1900
|
-
escalationTriggers: ['active deploy exceeds timeout', 'active deploy fails without a release log']
|
|
1901
|
-
};
|
|
1902
|
-
}
|
|
1903
|
-
if (input.action === 'deploy_new_artifact_after_release_gate') {
|
|
1904
|
-
return {
|
|
1905
|
-
planId: `${surface}:deploy_new_artifact_after_release_gate`,
|
|
1906
|
-
label: 'Deploy New Artifact After Release Gate',
|
|
1907
|
-
recommendedChannel: 'new_artifact_deploy',
|
|
1908
|
-
reason: input.reason,
|
|
1909
|
-
fullDeployAvoided: false,
|
|
1910
|
-
steps: [
|
|
1911
|
-
hotfixStep('verify_new_artifact_fingerprint', 'Verify new artifact fingerprint', 'new_artifact_deploy', {
|
|
1912
|
-
safeToAutoRun: true,
|
|
1913
|
-
requiresFullDeploy: true,
|
|
1914
|
-
requiredEvidence: ['current artifact fingerprint differs from last deployed fingerprint'],
|
|
1915
|
-
successEvidence: ['release gate permits one full deploy']
|
|
1916
|
-
})
|
|
1917
|
-
],
|
|
1918
|
-
acceptanceEvidence: [
|
|
1919
|
-
'new artifact fingerprint recorded',
|
|
1920
|
-
'release gate passed before full deploy',
|
|
1921
|
-
'deploy/publish result recorded'
|
|
1922
|
-
],
|
|
1923
|
-
escalationTriggers: ['artifact fingerprint matches last deploy', 'release gate fails']
|
|
1924
|
-
};
|
|
1925
|
-
}
|
|
1926
|
-
if (input.action === 'build_artifact_first') {
|
|
1927
|
-
return {
|
|
1928
|
-
planId: `${surface}:build_artifact_first`,
|
|
1929
|
-
label: 'Build Artifact First',
|
|
1930
|
-
recommendedChannel: 'artifact_build',
|
|
1931
|
-
reason: input.reason,
|
|
1932
|
-
fullDeployAvoided: true,
|
|
1933
|
-
steps: [
|
|
1934
|
-
hotfixStep('build_once_record_fingerprint', 'Build once and record fingerprint', 'artifact_build', {
|
|
1935
|
-
safeToAutoRun: false,
|
|
1936
|
-
requiredEvidence: ['missing artifact fingerprint', 'release gate reason for building'],
|
|
1937
|
-
successEvidence: ['artifact fingerprint recorded before any deploy']
|
|
1938
|
-
})
|
|
1939
|
-
],
|
|
1940
|
-
acceptanceEvidence: ['artifact fingerprint exists', 'no deploy queued before release gate'],
|
|
1941
|
-
escalationTriggers: ['build repeats without a new fingerprint', 'build failure lacks compile log']
|
|
1942
|
-
};
|
|
1943
|
-
}
|
|
1944
|
-
if (input.action === 'review_force_deploy') {
|
|
1945
|
-
return {
|
|
1946
|
-
planId: `${surface}:review_force_deploy`,
|
|
1947
|
-
label: 'Review Force Deploy',
|
|
1948
|
-
recommendedChannel: 'force_deploy_review',
|
|
1949
|
-
reason: input.reason,
|
|
1950
|
-
fullDeployAvoided: false,
|
|
1951
|
-
steps: [
|
|
1952
|
-
hotfixStep('verify_force_deploy_reason', 'Verify force deploy reason', 'force_deploy_review', {
|
|
1953
|
-
requiredEvidence: ['explicit force_deploy reason', 'why hotfix cannot resolve the release blocker'],
|
|
1954
|
-
successEvidence: ['one allowed deploy attempt is recorded with reason']
|
|
1955
|
-
})
|
|
1956
|
-
],
|
|
1957
|
-
acceptanceEvidence: ['force deploy evidence recorded', 'duplicate fingerprint exception is auditable'],
|
|
1958
|
-
escalationTriggers: ['force deploy reason is empty', 'same force deploy fails twice']
|
|
1959
|
-
};
|
|
1960
|
-
}
|
|
1961
|
-
return {
|
|
1962
|
-
planId: `${surface}:${input.action}`,
|
|
1963
|
-
label: input.action === 'block_duplicate_deploy'
|
|
1964
|
-
? 'Hotfix Or Force Deploy'
|
|
1965
|
-
: input.action === 'block_duplicate_publish'
|
|
1966
|
-
? 'Hotfix Or Force Publish'
|
|
1967
|
-
: 'Hotfix Release Artifact',
|
|
1968
|
-
recommendedChannel: input.action === 'block_duplicate_publish'
|
|
1969
|
-
? 'release_artifact'
|
|
1970
|
-
: input.releaseBlocked ? 'release_artifact' : 'static_ui',
|
|
1971
|
-
reason: input.reason,
|
|
1972
|
-
fullDeployAvoided: true,
|
|
1973
|
-
steps: [
|
|
1974
|
-
hotfixStep('static_ui_hotfix', 'Static UI hotfix', 'static_ui', {
|
|
1975
|
-
safeToAutoRun: true,
|
|
1976
|
-
requiredEvidence: ['sourceCommitSha, githubCommitUrl, and passed gitPushStatus', 'built dist path', 'target S3 bucket and CloudFront distribution', 'changed frontend bundle or asset list'],
|
|
1977
|
-
successEvidence: ['GitHub commit and push proof', 'S3 upload result', 'CloudFront invalidation id', 'public bundle contains expected hotfix marker']
|
|
1978
|
-
}),
|
|
1979
|
-
hotfixStep('backend_js_hotfix', 'Backend JS hotfix', 'backend_js', {
|
|
1980
|
-
requiredEvidence: ['sourceCommitSha, githubCommitUrl, and passed gitPushStatus', 'compiled JS artifact path', 'target backend host', 'diff limited to runner/operator code'],
|
|
1981
|
-
successEvidence: ['GitHub commit and push proof', 'remote file checksum', 'service restart or process reload evidence', 'self-test passes']
|
|
1982
|
-
}),
|
|
1983
|
-
hotfixStep('config_seed_cache_restart', 'Config, seed, cache, or restart repair', 'config', {
|
|
1984
|
-
safeToAutoRun: true,
|
|
1985
|
-
requiredEvidence: ['sourceCommitSha, githubCommitUrl, and passed gitPushStatus', 'exact config/seed/cache key or process name', 'why no product artifact changed'],
|
|
1986
|
-
successEvidence: ['GitHub commit and push proof', 'release gate rerun result', 'operator console release snapshot refreshed']
|
|
1987
|
-
})
|
|
1988
|
-
],
|
|
1989
|
-
acceptanceEvidence: commonAcceptance,
|
|
1990
|
-
escalationTriggers: [
|
|
1991
|
-
'hotfix changes customer-facing product code outside the release artifact',
|
|
1992
|
-
'same release gate fails twice after hotfix evidence',
|
|
1993
|
-
'same publish fingerprint is retried without force_publish evidence',
|
|
1994
|
-
'force deploy is requested without a reason'
|
|
1995
|
-
]
|
|
1996
|
-
};
|
|
1997
|
-
}
|
|
1998
|
-
|
|
1999
|
-
export function buildResolveIOAIManagerHotfixFirstReleasePolicy(
|
|
2000
|
-
input: ResolveIOAIManagerHotfixFirstReleasePolicyInput = {}
|
|
2001
|
-
): ResolveIOAIManagerHotfixFirstReleasePolicy {
|
|
2002
|
-
const surface = cleanText(input.surface || 'runner', 120);
|
|
2003
|
-
const deployStatus = cleanText(input.deployStatus, 160);
|
|
2004
|
-
const publishStatus = cleanText(input.publishStatus, 160);
|
|
2005
|
-
const sampleDataStatus = cleanText(input.sampleDataStatus, 160);
|
|
2006
|
-
const deployFingerprint = cleanText(input.deployFingerprint, 240);
|
|
2007
|
-
const lastDeployFingerprint = cleanText(input.lastDeployFingerprint, 240);
|
|
2008
|
-
const publishFingerprint = cleanText(input.publishFingerprint, 240);
|
|
2009
|
-
const lastPublishFingerprint = cleanText(input.lastPublishFingerprint, 240);
|
|
2010
|
-
const fingerprintMatchesLast = !!deployFingerprint && !!lastDeployFingerprint && deployFingerprint === lastDeployFingerprint;
|
|
2011
|
-
const publishFingerprintMatchesLast = !!publishFingerprint && !!lastPublishFingerprint && publishFingerprint === lastPublishFingerprint;
|
|
2012
|
-
const releaseBlocked = [deployStatus, publishStatus, sampleDataStatus].some(releaseStatusIsBlocked);
|
|
2013
|
-
const duplicateDeployBlocked = fingerprintMatchesLast
|
|
2014
|
-
&& input.forceDeploy !== true
|
|
2015
|
-
&& releaseStatusIsTerminalDeploy([deployStatus, publishStatus].filter(Boolean).join(' '));
|
|
2016
|
-
const duplicatePublishBlocked = publishFingerprintMatchesLast
|
|
2017
|
-
&& input.forcePublish !== true
|
|
2018
|
-
&& releaseStatusIsBlocked(publishStatus)
|
|
2019
|
-
&& releaseStatusIsTerminalDeploy(publishStatus);
|
|
2020
|
-
let recommendedAction: ResolveIOAIManagerHotfixFirstReleaseAction = 'hotfix_release_artifact';
|
|
2021
|
-
let label = 'Hotfix Release Artifact';
|
|
2022
|
-
let reason = 'Repair the live runner/operator code, release config, domain, seed-data, or release artifact and rerun only the failed release gate.';
|
|
2023
|
-
let fullDeployAllowed = false;
|
|
2024
|
-
if (input.activeDeploy === true) {
|
|
2025
|
-
recommendedAction = 'wait_for_active_deploy';
|
|
2026
|
-
label = 'Wait For Active Deploy';
|
|
2027
|
-
reason = 'A deploy is already queued or running; collect that result before spending another deploy cycle.';
|
|
2028
|
-
}
|
|
2029
|
-
else if (duplicateDeployBlocked) {
|
|
2030
|
-
recommendedAction = 'block_duplicate_deploy';
|
|
2031
|
-
label = 'Block Duplicate Deploy';
|
|
2032
|
-
reason = 'The same artifact fingerprint already has release status; hotfix or provide explicit force_deploy evidence before rerunning it.';
|
|
2033
|
-
}
|
|
2034
|
-
else if (duplicatePublishBlocked) {
|
|
2035
|
-
recommendedAction = 'block_duplicate_publish';
|
|
2036
|
-
label = 'Block Duplicate Publish';
|
|
2037
|
-
reason = 'The same publish fingerprint already has release status; hotfix the release artifact or provide explicit force_publish evidence before rerunning publish.';
|
|
2038
|
-
}
|
|
2039
|
-
else if ((fingerprintMatchesLast && input.forceDeploy === true) || (publishFingerprintMatchesLast && input.forcePublish === true)) {
|
|
2040
|
-
recommendedAction = 'review_force_deploy';
|
|
2041
|
-
label = 'Review Force Deploy';
|
|
2042
|
-
reason = input.forcePublish === true
|
|
2043
|
-
? 'force_publish was explicitly requested for a repeated publish artifact; require a reason and rerun only the failed release gate.'
|
|
2044
|
-
: 'force_deploy was explicitly requested for a repeated artifact; require a reason and rerun only the failed release gate.';
|
|
2045
|
-
fullDeployAllowed = true;
|
|
2046
|
-
}
|
|
2047
|
-
else if (input.hasNewArtifact === false && !releaseBlocked) {
|
|
2048
|
-
recommendedAction = 'build_artifact_first';
|
|
2049
|
-
label = 'Build Artifact First';
|
|
2050
|
-
reason = 'No release artifact fingerprint is available yet; build once, record the fingerprint, and avoid repeated deploys.';
|
|
2051
|
-
}
|
|
2052
|
-
else if (input.hasNewArtifact === true && !releaseBlocked) {
|
|
2053
|
-
recommendedAction = 'deploy_new_artifact_after_release_gate';
|
|
2054
|
-
label = 'Deploy New Artifact After Release Gate';
|
|
2055
|
-
reason = 'A new artifact can be deployed after the release gate proves the artifact is new and ready.';
|
|
2056
|
-
fullDeployAllowed = true;
|
|
2057
|
-
}
|
|
2058
|
-
const allowedActions = buildReleaseAllowedActions(recommendedAction);
|
|
2059
|
-
const hotfixPlan = buildHotfixFirstReleasePlan({
|
|
2060
|
-
action: recommendedAction,
|
|
2061
|
-
surface,
|
|
2062
|
-
reason,
|
|
2063
|
-
releaseBlocked
|
|
2064
|
-
});
|
|
2065
|
-
return {
|
|
2066
|
-
policy: 'hotfix_first',
|
|
2067
|
-
surface,
|
|
2068
|
-
recommendedAction,
|
|
2069
|
-
label,
|
|
2070
|
-
reason,
|
|
2071
|
-
hotfixPreferred: recommendedAction !== 'deploy_new_artifact_after_release_gate' && recommendedAction !== 'review_force_deploy',
|
|
2072
|
-
fullDeployAllowed,
|
|
2073
|
-
duplicateDeployBlocked,
|
|
2074
|
-
duplicatePublishBlocked,
|
|
2075
|
-
forceDeployRequiredToRepeat: true,
|
|
2076
|
-
forcePublishRequiredToRepeat: true,
|
|
2077
|
-
statuses: {
|
|
2078
|
-
deploy: deployStatus,
|
|
2079
|
-
publish: publishStatus,
|
|
2080
|
-
sampleData: sampleDataStatus
|
|
2081
|
-
},
|
|
2082
|
-
fingerprints: {
|
|
2083
|
-
current: deployFingerprint,
|
|
2084
|
-
last: lastDeployFingerprint,
|
|
2085
|
-
matchesLast: fingerprintMatchesLast,
|
|
2086
|
-
publishCurrent: publishFingerprint,
|
|
2087
|
-
publishLast: lastPublishFingerprint,
|
|
2088
|
-
publishMatchesLast: publishFingerprintMatchesLast
|
|
2089
|
-
},
|
|
2090
|
-
hotfixPlan,
|
|
2091
|
-
allowedActions,
|
|
2092
|
-
forbiddenActions: [
|
|
2093
|
-
'rerun full builder loop for a release-only blocker',
|
|
2094
|
-
'queue duplicate deploy without force_deploy evidence',
|
|
2095
|
-
'queue duplicate publish without force_publish evidence',
|
|
2096
|
-
'publish/deploy again before reading the failed release log',
|
|
2097
|
-
'change core workflow after business proof passed unless release evidence proves it is required'
|
|
2098
|
-
],
|
|
2099
|
-
requiredEvidence: [
|
|
2100
|
-
'failed release gate log or active deploy result',
|
|
2101
|
-
'deploy artifact fingerprint comparison',
|
|
2102
|
-
'publish artifact fingerprint comparison when publish is the failed gate',
|
|
2103
|
-
'hotfix/config/seed/domain change evidence when no new product artifact is needed',
|
|
2104
|
-
'force_deploy or force_publish reason when repeating the same fingerprint'
|
|
2105
|
-
]
|
|
2106
|
-
};
|
|
2107
|
-
}
|
|
2108
|
-
|
|
2109
|
-
export function isResolveIOAIManagerSafeAutoDispatch(
|
|
2110
|
-
input: ResolveIOAIManagerAutoDispatchPolicyInput | ResolveIOAIManagerRecoveryActionDispatchAction | string | undefined
|
|
2111
|
-
): boolean {
|
|
2112
|
-
const normalizedInput = typeof input === 'string'
|
|
2113
|
-
? { dispatchAction: input }
|
|
2114
|
-
: (input || {});
|
|
2115
|
-
const dispatchAction = cleanText(normalizedInput.dispatchAction || normalizedInput.directive?.dispatchAction, 120);
|
|
2116
|
-
const directiveAllowed = normalizedInput.directive?.allowed;
|
|
2117
|
-
const actionAutoRunnable = normalizedInput.action?.autoRunnable;
|
|
2118
|
-
const productRepairAllowed = normalizedInput.directive?.canRunProductRepair === true
|
|
2119
|
-
|| normalizedInput.directive?.productRepairAllowed === true
|
|
2120
|
-
|| normalizedInput.action?.productRepairAllowed === true;
|
|
2121
|
-
const expensiveModelAllowed = normalizedInput.directive?.canRunExpensiveModel === true
|
|
2122
|
-
|| normalizedInput.directive?.expensiveModelAllowed === true
|
|
2123
|
-
|| normalizedInput.action?.expensiveModelAllowed === true;
|
|
2124
|
-
if (!dispatchAction || dispatchAction === 'park_manual') {
|
|
2125
|
-
return false;
|
|
2126
|
-
}
|
|
2127
|
-
if (directiveAllowed === false || actionAutoRunnable === false) {
|
|
2128
|
-
return false;
|
|
2129
|
-
}
|
|
2130
|
-
if ([
|
|
2131
|
-
'run_evidence_probe',
|
|
2132
|
-
'run_read_only_diagnosis',
|
|
2133
|
-
'run_infra_repair',
|
|
2134
|
-
'run_compile_repair'
|
|
2135
|
-
].includes(dispatchAction)) {
|
|
2136
|
-
return true;
|
|
2137
|
-
}
|
|
2138
|
-
if (dispatchAction === 'run_release_repair') {
|
|
2139
|
-
return normalizedInput.includeReleaseRepair !== false;
|
|
2140
|
-
}
|
|
2141
|
-
if (dispatchAction === 'run_journey_contract_repair') {
|
|
2142
|
-
return normalizedInput.includeJourneyContractRepair !== false && productRepairAllowed !== true;
|
|
2143
|
-
}
|
|
2144
|
-
if (dispatchAction === 'run_business_assertion_repair') {
|
|
2145
|
-
return normalizedInput.includeBusinessProofRepair === true
|
|
2146
|
-
&& productRepairAllowed !== true
|
|
2147
|
-
&& expensiveModelAllowed !== true;
|
|
2148
|
-
}
|
|
2149
|
-
if (dispatchAction === 'run_targeted_product_repair') {
|
|
2150
|
-
return normalizedInput.includeProductRepair === true
|
|
2151
|
-
&& productRepairAllowed === true
|
|
2152
|
-
&& expensiveModelAllowed !== true;
|
|
2153
|
-
}
|
|
2154
|
-
return dispatchAction === 'advance' || dispatchAction === 'continue_gate';
|
|
2155
|
-
}
|
|
2156
|
-
|
|
2157
|
-
function isoNow(value?: Date | string): string {
|
|
2158
|
-
if (value instanceof Date) {
|
|
2159
|
-
return value.toISOString();
|
|
2160
|
-
}
|
|
2161
|
-
const parsed = value ? new Date(value) : new Date();
|
|
2162
|
-
if (Number.isFinite(parsed.getTime())) {
|
|
2163
|
-
return parsed.toISOString();
|
|
2164
|
-
}
|
|
2165
|
-
return new Date().toISOString();
|
|
2166
|
-
}
|
|
2167
|
-
|
|
2168
|
-
function stableHash(prefix: string, value: any): string {
|
|
2169
|
-
const normalized = typeof value === 'string'
|
|
2170
|
-
? cleanText(value, 12000)
|
|
2171
|
-
: JSON.stringify(value || {});
|
|
2172
|
-
let hash = 0;
|
|
2173
|
-
for (let index = 0; index < normalized.length; index += 1) {
|
|
2174
|
-
hash = ((hash << 5) - hash + normalized.charCodeAt(index)) | 0;
|
|
2175
|
-
}
|
|
2176
|
-
return `${prefix}-${Math.abs(hash).toString(36) || '0'}`;
|
|
2177
|
-
}
|
|
2178
|
-
|
|
2179
|
-
export function normalizeResolveIOAIManagerFailureClass(value: any): string {
|
|
2180
|
-
const normalized = cleanText(value, 80)
|
|
2181
|
-
.toLowerCase()
|
|
2182
|
-
.replace(/[\s-]+/g, '_');
|
|
2183
|
-
if (!normalized) {
|
|
2184
|
-
return 'unknown';
|
|
2185
|
-
}
|
|
2186
|
-
if (/^(qa_infra|infrastructure|harness|puppeteer|chrome|mongo|port|startup)$/.test(normalized)) {
|
|
2187
|
-
return 'infra';
|
|
2188
|
-
}
|
|
2189
|
-
if (/^(build|compile|typescript|tsc|angular_build|ng_build)$/.test(normalized)) {
|
|
2190
|
-
return 'compile';
|
|
2191
|
-
}
|
|
2192
|
-
if (/^(false_pass|route_only|missing_business_proof)$/.test(normalized)) {
|
|
2193
|
-
return 'business';
|
|
2194
|
-
}
|
|
2195
|
-
return normalized.slice(0, 80);
|
|
2196
|
-
}
|
|
2197
|
-
|
|
2198
|
-
export function fingerprintResolveIOAIManagerBlocker(value: any): string {
|
|
2199
|
-
const normalized = cleanText(value, 4000)
|
|
2200
|
-
.toLowerCase()
|
|
2201
|
-
.replace(/[a-f0-9]{16,}/g, '<id>')
|
|
2202
|
-
.replace(/\b\d{2,}\b/g, '<n>')
|
|
2203
|
-
.replace(/\bline\s+<n>\b/g, 'line <n>')
|
|
2204
|
-
.replace(/https?:\/\/\S+/g, '<url>')
|
|
2205
|
-
.replace(/\s+/g, ' ')
|
|
2206
|
-
.trim();
|
|
2207
|
-
return stableHash('mgr-blocker', normalized);
|
|
2208
|
-
}
|
|
2209
|
-
|
|
2210
|
-
function resolveResolveIOAIManagerBlockerFingerprint(
|
|
2211
|
-
record: ResolveIOAIManagerFailureRecord | undefined,
|
|
2212
|
-
fallback?: any
|
|
2213
|
-
): string {
|
|
2214
|
-
const blockerText = cleanText(record?.blocker || record?.summary, 4000);
|
|
2215
|
-
if (blockerText) {
|
|
2216
|
-
return fingerprintResolveIOAIManagerBlocker(blockerText);
|
|
2217
|
-
}
|
|
2218
|
-
const explicit = cleanText(record?.blockerFingerprint, 120);
|
|
2219
|
-
if (explicit) {
|
|
2220
|
-
return explicit;
|
|
2221
|
-
}
|
|
2222
|
-
return fingerprintResolveIOAIManagerBlocker(fallback || '');
|
|
2223
|
-
}
|
|
2224
|
-
|
|
2225
|
-
export function hashResolveIOAIManagerEvidence(record: ResolveIOAIManagerFailureRecord | undefined): string {
|
|
2226
|
-
if (!record) {
|
|
2227
|
-
return stableHash('mgr-evidence', '');
|
|
2228
|
-
}
|
|
2229
|
-
const explicit = cleanText(record.evidenceHash, 120);
|
|
2230
|
-
if (explicit) {
|
|
2231
|
-
return explicit;
|
|
2232
|
-
}
|
|
2233
|
-
return stableHash('mgr-evidence', {
|
|
2234
|
-
failureClass: normalizeResolveIOAIManagerFailureClass(record.failureClass),
|
|
2235
|
-
blocker: cleanText(record.blocker || record.summary, 2000),
|
|
2236
|
-
changedFiles: cleanList(record.changedFiles, 80, 500).sort(),
|
|
2237
|
-
artifactPaths: cleanList(record.artifactPaths, 80, 500).sort()
|
|
2238
|
-
});
|
|
2239
|
-
}
|
|
2240
|
-
|
|
2241
|
-
export function buildResolveIOAIManagerRecoveryCheckpoint(
|
|
2242
|
-
input: ResolveIOAIManagerRecoveryCheckpointInput
|
|
2243
|
-
): ResolveIOAIManagerRecoveryCheckpoint {
|
|
2244
|
-
const plan = input.plan;
|
|
2245
|
-
const current = input.current || {};
|
|
2246
|
-
const blockerFingerprint = resolveResolveIOAIManagerBlockerFingerprint(current, plan.objective);
|
|
2247
|
-
const evidenceHash = hashResolveIOAIManagerEvidence(current);
|
|
2248
|
-
const changedFiles = cleanList(current.changedFiles, 80, 500);
|
|
2249
|
-
const artifactPaths = cleanList(current.artifactPaths, 80, 500);
|
|
2250
|
-
const previous = input.previousCheckpoint;
|
|
2251
|
-
const sameCheckpointBase = previous
|
|
2252
|
-
&& previous.recoveryClass === plan.recoveryClass
|
|
2253
|
-
&& previous.lane === plan.lane
|
|
2254
|
-
&& previous.stepType === plan.stepType
|
|
2255
|
-
&& previous.blockerFingerprint === blockerFingerprint
|
|
2256
|
-
&& previous.evidenceHash === evidenceHash;
|
|
2257
|
-
const attempts = sameCheckpointBase ? Math.max(0, Number(previous.attempts || 0)) + 1 : 1;
|
|
2258
|
-
const status: ResolveIOAIManagerRecoveryCheckpointStatus = plan.recoveryClass === 'manual_handoff'
|
|
2259
|
-
? 'manual_handoff'
|
|
2260
|
-
: plan.recoveryClass === 'blocked_until_new_evidence'
|
|
2261
|
-
? 'parked'
|
|
2262
|
-
: plan.recoveryClass === 'advance_after_proof'
|
|
2263
|
-
? 'complete'
|
|
2264
|
-
: 'active';
|
|
2265
|
-
const now = isoNow(input.now);
|
|
2266
|
-
const checkpointId = stableHash('mgr-recovery', {
|
|
2267
|
-
recoveryClass: plan.recoveryClass,
|
|
2268
|
-
lane: plan.lane,
|
|
2269
|
-
stepType: plan.stepType,
|
|
2270
|
-
allowedAction: plan.allowedAction,
|
|
2271
|
-
blockerFingerprint,
|
|
2272
|
-
evidenceHash
|
|
2273
|
-
});
|
|
2274
|
-
return {
|
|
2275
|
-
checkpointId,
|
|
2276
|
-
recoveryClass: plan.recoveryClass,
|
|
2277
|
-
status,
|
|
2278
|
-
lane: plan.lane,
|
|
2279
|
-
stepType: plan.stepType,
|
|
2280
|
-
allowedAction: plan.allowedAction,
|
|
2281
|
-
productRepairAllowed: plan.productRepairAllowed,
|
|
2282
|
-
expensiveModelAllowed: plan.expensiveModelAllowed,
|
|
2283
|
-
attempts,
|
|
2284
|
-
maxAttemptsBeforePark: plan.maxAttemptsBeforePark,
|
|
2285
|
-
blockerFingerprint,
|
|
2286
|
-
evidenceHash,
|
|
2287
|
-
changedFiles,
|
|
2288
|
-
artifactPaths,
|
|
2289
|
-
requiredEvidence: cleanList(plan.requiredEvidence, 40, 500),
|
|
2290
|
-
loopResetEvidence: cleanList(plan.loopResetEvidence, 40, 500),
|
|
2291
|
-
forbiddenActions: cleanList(plan.forbiddenActions, 40, 500),
|
|
2292
|
-
objective: cleanText(plan.objective, 1000),
|
|
2293
|
-
nextActionLabel: cleanText(plan.nextActionLabel, 160),
|
|
2294
|
-
createdAt: sameCheckpointBase && previous?.createdAt ? previous.createdAt : now,
|
|
2295
|
-
updatedAt: now
|
|
2296
|
-
};
|
|
2297
|
-
}
|
|
2298
|
-
|
|
2299
|
-
function makeRecoveryEvidenceProbeStep(
|
|
2300
|
-
id: string,
|
|
2301
|
-
kind: ResolveIOAIManagerRecoveryEvidenceProbeStepKind,
|
|
2302
|
-
label: string,
|
|
2303
|
-
objective: string,
|
|
2304
|
-
artifactExpectation: string,
|
|
2305
|
-
successSignal: string,
|
|
2306
|
-
options: Partial<Pick<ResolveIOAIManagerRecoveryEvidenceProbeStep, 'required' | 'commandHint'>> = {}
|
|
2307
|
-
): ResolveIOAIManagerRecoveryEvidenceProbeStep {
|
|
2308
|
-
return {
|
|
2309
|
-
id,
|
|
2310
|
-
kind,
|
|
2311
|
-
label,
|
|
2312
|
-
objective: cleanText(objective, 1000),
|
|
2313
|
-
required: options.required !== false,
|
|
2314
|
-
...(options.commandHint ? { commandHint: cleanText(options.commandHint, 500) } : {}),
|
|
2315
|
-
artifactExpectation: cleanText(artifactExpectation, 500),
|
|
2316
|
-
successSignal: cleanText(successSignal, 500)
|
|
2317
|
-
};
|
|
2318
|
-
}
|
|
2319
|
-
|
|
2320
|
-
function recoveryEvidenceProbeStepsFor(
|
|
2321
|
-
checkpoint: ResolveIOAIManagerRecoveryCheckpoint
|
|
2322
|
-
): ResolveIOAIManagerRecoveryEvidenceProbeStep[] {
|
|
2323
|
-
const base = [
|
|
2324
|
-
makeRecoveryEvidenceProbeStep(
|
|
2325
|
-
'snapshot_current_blocker',
|
|
2326
|
-
'rerun_same_gate',
|
|
2327
|
-
'Snapshot Current Blocker',
|
|
2328
|
-
'Capture the current blocker, failure class, evidence hash, changed files, and artifact paths before any new repair.',
|
|
2329
|
-
'runner-evidence/current-blocker.json',
|
|
2330
|
-
'Snapshot includes the same checkpointId, blockerFingerprint, and evidenceHashBefore.'
|
|
2331
|
-
)
|
|
2332
|
-
];
|
|
2333
|
-
if (checkpoint.recoveryClass === 'diagnosis_only' || checkpoint.recoveryClass === 'diagnosis_revision') {
|
|
2334
|
-
return [
|
|
2335
|
-
...base,
|
|
2336
|
-
makeRecoveryEvidenceProbeStep(
|
|
2337
|
-
'collect_read_only_root_cause_evidence',
|
|
2338
|
-
'read_only_diagnosis',
|
|
2339
|
-
'Collect Read-Only Root-Cause Evidence',
|
|
2340
|
-
checkpoint.recoveryClass === 'diagnosis_revision'
|
|
2341
|
-
? 'Collect new evidence that proves the current owner_files or failing_path is wrong before broadening scope.'
|
|
2342
|
-
: 'Reproduce or classify the issue and record the exact customer complaint, expected result, observed result, route/module, and account context.',
|
|
2343
|
-
'runner-evidence/diagnosis-readonly.md',
|
|
2344
|
-
'Evidence supports one falsifiable accepted hypothesis and at least one rejected alternative.'
|
|
2345
|
-
),
|
|
2346
|
-
makeRecoveryEvidenceProbeStep(
|
|
2347
|
-
'validate_diagnosis_gate',
|
|
2348
|
-
'read_only_diagnosis',
|
|
2349
|
-
'Validate Diagnosis Gate',
|
|
2350
|
-
'Validate issue_case, accepted_hypothesis, rejected_alternatives, failing_path, owner_files, and before/action/after proof_plan.',
|
|
2351
|
-
'runner-evidence/diagnosis-gate.json',
|
|
2352
|
-
'Diagnosis gate validates before product-code repair is allowed.'
|
|
2353
|
-
)
|
|
2354
|
-
];
|
|
2355
|
-
}
|
|
2356
|
-
if (checkpoint.recoveryClass === 'infra_repair') {
|
|
2357
|
-
return [
|
|
2358
|
-
...base,
|
|
2359
|
-
makeRecoveryEvidenceProbeStep(
|
|
2360
|
-
'run_infra_preflight',
|
|
2361
|
-
'infra_preflight',
|
|
2362
|
-
'Run Infra Preflight',
|
|
2363
|
-
'Check Puppeteer/Chrome, ports, Mongo/settings, cache state, startup commands, and server/client process health.',
|
|
2364
|
-
'runner-evidence/infra-preflight.log',
|
|
2365
|
-
'The same infra gate either passes or returns a new infra blocker hash.',
|
|
2366
|
-
{ commandHint: 'runner preflight: puppeteer/chrome/mongo/ports/startup' }
|
|
2367
|
-
)
|
|
2368
|
-
];
|
|
2369
|
-
}
|
|
2370
|
-
if (checkpoint.recoveryClass === 'compile_repair') {
|
|
2371
|
-
return [
|
|
2372
|
-
...base,
|
|
2373
|
-
makeRecoveryEvidenceProbeStep(
|
|
2374
|
-
'rerun_compile_gate',
|
|
2375
|
-
'compile_check',
|
|
2376
|
-
'Rerun Compile Gate',
|
|
2377
|
-
'Rerun the same deterministic compile/build command and capture the first actionable error.',
|
|
2378
|
-
'runner-evidence/compile.log',
|
|
2379
|
-
'Compile passes or the first failing error changes.',
|
|
2380
|
-
{ commandHint: 'same finite build/compile command from the failed gate' }
|
|
2381
|
-
)
|
|
2382
|
-
];
|
|
2383
|
-
}
|
|
2384
|
-
if (checkpoint.recoveryClass === 'journey_contract_repair') {
|
|
2385
|
-
return [
|
|
2386
|
-
...base,
|
|
2387
|
-
makeRecoveryEvidenceProbeStep(
|
|
2388
|
-
'validate_journey_contract',
|
|
2389
|
-
'journey_validation',
|
|
2390
|
-
'Validate Journey Contract',
|
|
2391
|
-
'Validate docs/APP_JOURNEY_CONTRACT.md has first/next/last workflow, route/action/method mapping, seeded data story, completion states, and QA assertions.',
|
|
2392
|
-
'runner-evidence/journey-contract-validation.json',
|
|
2393
|
-
'Journey contract validates before build tasks continue.'
|
|
2394
|
-
),
|
|
2395
|
-
makeRecoveryEvidenceProbeStep(
|
|
2396
|
-
'verify_hub_workflow_mapping',
|
|
2397
|
-
'journey_validation',
|
|
2398
|
-
'Verify Hub Workflow Mapping',
|
|
2399
|
-
'Confirm the hub CTA and screen sequence implement the journey contract instead of link-only navigation.',
|
|
2400
|
-
'runner-evidence/hub-workflow-map.json',
|
|
2401
|
-
'Primary hub action maps to a real route/action/method/calculation and next state.'
|
|
2402
|
-
)
|
|
2403
|
-
];
|
|
2404
|
-
}
|
|
2405
|
-
if (checkpoint.recoveryClass === 'business_proof_repair' || checkpoint.recoveryClass === 'product_code_repair') {
|
|
2406
|
-
return [
|
|
2407
|
-
...base,
|
|
2408
|
-
makeRecoveryEvidenceProbeStep(
|
|
2409
|
-
'rerun_business_assertion',
|
|
2410
|
-
'business_assertion',
|
|
2411
|
-
'Rerun Business Assertion',
|
|
2412
|
-
'Run the exact issue/app promise proof and capture before/action/after DOM/data evidence.',
|
|
2413
|
-
'runner-evidence/business-assertion.json',
|
|
2414
|
-
'Business assertion passes, or the observed failure changes with a new artifact path.'
|
|
2415
|
-
),
|
|
2416
|
-
makeRecoveryEvidenceProbeStep(
|
|
2417
|
-
'check_diff_scope',
|
|
2418
|
-
'diff_scope_check',
|
|
2419
|
-
'Check Diff Scope',
|
|
2420
|
-
'Compare changed files against the current diagnosis/journey owner scope before another code repair.',
|
|
2421
|
-
'runner-evidence/diff-scope.json',
|
|
2422
|
-
'Changed files are in scope, or diagnosis/journey scope is revised with evidence.'
|
|
2423
|
-
)
|
|
2424
|
-
];
|
|
2425
|
-
}
|
|
2426
|
-
if (checkpoint.recoveryClass === 'release_repair') {
|
|
2427
|
-
return [
|
|
2428
|
-
...base,
|
|
2429
|
-
makeRecoveryEvidenceProbeStep(
|
|
2430
|
-
'collect_release_status',
|
|
2431
|
-
'release_status',
|
|
2432
|
-
'Collect Release Status',
|
|
2433
|
-
'Collect publish/deploy/build-instance/domain/CloudFront status and the last actionable deploy error.',
|
|
2434
|
-
'runner-evidence/release-status.json',
|
|
2435
|
-
'Release gate passes or returns a new release blocker hash without product-code repair.'
|
|
2436
|
-
)
|
|
2437
|
-
];
|
|
2438
|
-
}
|
|
2439
|
-
if (checkpoint.recoveryClass === 'manual_handoff') {
|
|
2440
|
-
return [
|
|
2441
|
-
...base,
|
|
2442
|
-
makeRecoveryEvidenceProbeStep(
|
|
2443
|
-
'operator_review_checkpoint',
|
|
2444
|
-
'operator_review',
|
|
2445
|
-
'Operator Review Checkpoint',
|
|
2446
|
-
'Expose budget, blocker, evidence hash, and the minimum policy/scope change needed before another expensive loop.',
|
|
2447
|
-
'runner-evidence/operator-review.json',
|
|
2448
|
-
'Operator approves a new scope/policy or provides new external evidence.'
|
|
2449
|
-
)
|
|
2450
|
-
];
|
|
2451
|
-
}
|
|
2452
|
-
return [
|
|
2453
|
-
...base,
|
|
2454
|
-
makeRecoveryEvidenceProbeStep(
|
|
2455
|
-
'rerun_same_deterministic_gate',
|
|
2456
|
-
'rerun_same_gate',
|
|
2457
|
-
'Rerun Same Deterministic Gate',
|
|
2458
|
-
'Rerun the exact failed check and capture new logs/artifacts without changing product code first.',
|
|
2459
|
-
'runner-evidence/retry-gate.log',
|
|
2460
|
-
'Gate passes, or evidenceHash/blockerFingerprint/artifactPaths change.'
|
|
2461
|
-
)
|
|
2462
|
-
];
|
|
2463
|
-
}
|
|
2464
|
-
|
|
2465
|
-
export function buildResolveIOAIManagerRecoveryEvidenceProbe(
|
|
2466
|
-
input: ResolveIOAIManagerRecoveryEvidenceProbeInput
|
|
2467
|
-
): ResolveIOAIManagerRecoveryEvidenceProbe {
|
|
2468
|
-
const checkpoint = input.checkpoint;
|
|
2469
|
-
const now = isoNow(input.now);
|
|
2470
|
-
const steps = recoveryEvidenceProbeStepsFor(checkpoint);
|
|
2471
|
-
const requiredArtifacts = Array.from(new Set(steps
|
|
2472
|
-
.filter((step) => step.required)
|
|
2473
|
-
.map((step) => step.artifactExpectation)
|
|
2474
|
-
.filter(Boolean)));
|
|
2475
|
-
return {
|
|
2476
|
-
probeId: stableHash('mgr-probe', {
|
|
2477
|
-
checkpointId: checkpoint.checkpointId,
|
|
2478
|
-
recoveryClass: checkpoint.recoveryClass,
|
|
2479
|
-
evidenceHash: checkpoint.evidenceHash,
|
|
2480
|
-
blockerFingerprint: checkpoint.blockerFingerprint
|
|
2481
|
-
}),
|
|
2482
|
-
checkpointId: checkpoint.checkpointId,
|
|
2483
|
-
recoveryClass: checkpoint.recoveryClass,
|
|
2484
|
-
lane: checkpoint.lane,
|
|
2485
|
-
stepType: checkpoint.stepType,
|
|
2486
|
-
objective: checkpoint.status === 'parked'
|
|
2487
|
-
? 'Collect new evidence before any additional expensive model/code repair.'
|
|
2488
|
-
: cleanText(checkpoint.objective, 1000),
|
|
2489
|
-
evidenceOnly: checkpoint.status === 'parked'
|
|
2490
|
-
|| checkpoint.recoveryClass === 'diagnosis_only'
|
|
2491
|
-
|| checkpoint.recoveryClass === 'diagnosis_revision'
|
|
2492
|
-
|| checkpoint.recoveryClass === 'infra_repair'
|
|
2493
|
-
|| checkpoint.recoveryClass === 'compile_repair'
|
|
2494
|
-
|| checkpoint.recoveryClass === 'release_repair',
|
|
2495
|
-
productRepairAllowed: checkpoint.productRepairAllowed && checkpoint.status !== 'parked',
|
|
2496
|
-
expensiveModelAllowed: checkpoint.expensiveModelAllowed && checkpoint.status !== 'parked',
|
|
2497
|
-
evidenceHashBefore: checkpoint.evidenceHash,
|
|
2498
|
-
blockerFingerprint: checkpoint.blockerFingerprint,
|
|
2499
|
-
steps,
|
|
2500
|
-
requiredArtifacts,
|
|
2501
|
-
acceptanceEvidence: Array.from(new Set([
|
|
2502
|
-
...checkpoint.requiredEvidence,
|
|
2503
|
-
...checkpoint.loopResetEvidence,
|
|
2504
|
-
...steps.map((step) => step.successSignal)
|
|
2505
|
-
].map((entry) => cleanText(entry, 500)).filter(Boolean))).slice(0, 30),
|
|
2506
|
-
stopConditions: Array.from(new Set([
|
|
2507
|
-
...checkpoint.forbiddenActions,
|
|
2508
|
-
'no new evidence hash',
|
|
2509
|
-
'no new artifact path',
|
|
2510
|
-
'product-code repair requested while evidenceOnly is true'
|
|
2511
|
-
].map((entry) => cleanText(entry, 500)).filter(Boolean))).slice(0, 30),
|
|
2512
|
-
loopResetEvidence: checkpoint.loopResetEvidence,
|
|
2513
|
-
createdAt: now
|
|
2514
|
-
};
|
|
2515
|
-
}
|
|
2516
|
-
|
|
2517
|
-
function recoveryAutomationModeFor(
|
|
2518
|
-
checkpoint: ResolveIOAIManagerRecoveryCheckpoint
|
|
2519
|
-
): ResolveIOAIManagerRecoveryAutomationMode {
|
|
2520
|
-
if (checkpoint.status === 'manual_handoff' || checkpoint.recoveryClass === 'manual_handoff') {
|
|
2521
|
-
return 'manual_review';
|
|
2522
|
-
}
|
|
2523
|
-
if (checkpoint.status === 'complete' || checkpoint.recoveryClass === 'advance_after_proof') {
|
|
2524
|
-
return 'advance';
|
|
2525
|
-
}
|
|
2526
|
-
if (checkpoint.status === 'parked' || checkpoint.recoveryClass === 'blocked_until_new_evidence') {
|
|
2527
|
-
return 'collect_evidence';
|
|
2528
|
-
}
|
|
2529
|
-
if (checkpoint.recoveryClass === 'diagnosis_only' || checkpoint.recoveryClass === 'diagnosis_revision') {
|
|
2530
|
-
return 'read_only_diagnosis';
|
|
2531
|
-
}
|
|
2532
|
-
if (checkpoint.recoveryClass === 'infra_repair') {
|
|
2533
|
-
return 'repair_infra';
|
|
2534
|
-
}
|
|
2535
|
-
if (checkpoint.recoveryClass === 'compile_repair') {
|
|
2536
|
-
return 'repair_compile';
|
|
2537
|
-
}
|
|
2538
|
-
if (checkpoint.recoveryClass === 'journey_contract_repair') {
|
|
2539
|
-
return 'repair_journey_contract';
|
|
2540
|
-
}
|
|
2541
|
-
if (checkpoint.recoveryClass === 'business_proof_repair') {
|
|
2542
|
-
return 'repair_business_assertion';
|
|
2543
|
-
}
|
|
2544
|
-
if (checkpoint.recoveryClass === 'release_repair') {
|
|
2545
|
-
return 'repair_release';
|
|
2546
|
-
}
|
|
2547
|
-
if (checkpoint.recoveryClass === 'product_code_repair') {
|
|
2548
|
-
return 'targeted_product_repair';
|
|
2549
|
-
}
|
|
2550
|
-
return 'continue_gate';
|
|
2551
|
-
}
|
|
2552
|
-
|
|
2553
|
-
function recoveryActionLabelFor(mode: ResolveIOAIManagerRecoveryAutomationMode, checkpoint: ResolveIOAIManagerRecoveryCheckpoint): string {
|
|
2554
|
-
const explicit = cleanText(checkpoint.nextActionLabel, 120);
|
|
2555
|
-
if (explicit && mode !== 'collect_evidence') {
|
|
2556
|
-
return explicit;
|
|
2557
|
-
}
|
|
2558
|
-
const labels: Record<ResolveIOAIManagerRecoveryAutomationMode, string> = {
|
|
2559
|
-
advance: 'Advance To Next Gate',
|
|
2560
|
-
collect_evidence: 'Collect New Evidence',
|
|
2561
|
-
read_only_diagnosis: 'Run Diagnosis Evidence Pass',
|
|
2562
|
-
repair_infra: 'Repair Infra Gate',
|
|
2563
|
-
repair_compile: 'Repair Compile Gate',
|
|
2564
|
-
repair_journey_contract: 'Repair Journey Contract',
|
|
2565
|
-
repair_business_assertion: 'Repair Business Proof',
|
|
2566
|
-
repair_release: 'Repair Release Gate',
|
|
2567
|
-
targeted_product_repair: 'Run Targeted Repair',
|
|
2568
|
-
manual_review: 'Manual Review',
|
|
2569
|
-
continue_gate: 'Continue Current Gate'
|
|
2570
|
-
};
|
|
2571
|
-
return labels[mode];
|
|
2572
|
-
}
|
|
2573
|
-
|
|
2574
|
-
function recoveryActionAutoRunnable(
|
|
2575
|
-
mode: ResolveIOAIManagerRecoveryAutomationMode,
|
|
2576
|
-
probe: ResolveIOAIManagerRecoveryEvidenceProbe
|
|
2577
|
-
): boolean {
|
|
2578
|
-
if (mode === 'manual_review') {
|
|
2579
|
-
return false;
|
|
2580
|
-
}
|
|
2581
|
-
if (mode === 'advance' || mode === 'continue_gate') {
|
|
2582
|
-
return true;
|
|
2583
|
-
}
|
|
2584
|
-
if (mode === 'targeted_product_repair' || mode === 'read_only_diagnosis' || mode === 'repair_journey_contract' || mode === 'repair_business_assertion') {
|
|
2585
|
-
return true;
|
|
2586
|
-
}
|
|
2587
|
-
return probe.steps.some((step) => step.commandHint || step.kind === 'rerun_same_gate' || step.kind === 'release_status' || step.kind === 'diff_scope_check');
|
|
2588
|
-
}
|
|
2589
|
-
|
|
2590
|
-
function recoveryActionCostCeilingUsd(mode: ResolveIOAIManagerRecoveryAutomationMode): number {
|
|
2591
|
-
const map: Record<ResolveIOAIManagerRecoveryAutomationMode, number> = {
|
|
2592
|
-
advance: 0,
|
|
2593
|
-
collect_evidence: 0.25,
|
|
2594
|
-
read_only_diagnosis: 1,
|
|
2595
|
-
repair_infra: 0.5,
|
|
2596
|
-
repair_compile: 1.5,
|
|
2597
|
-
repair_journey_contract: 2,
|
|
2598
|
-
repair_business_assertion: 1.5,
|
|
2599
|
-
repair_release: 0.75,
|
|
2600
|
-
targeted_product_repair: 5,
|
|
2601
|
-
manual_review: 0,
|
|
2602
|
-
continue_gate: 0
|
|
2603
|
-
};
|
|
2604
|
-
return map[mode] ?? 1;
|
|
2605
|
-
}
|
|
2606
|
-
|
|
2607
|
-
function recoveryActionRequiredStateTransition(mode: ResolveIOAIManagerRecoveryAutomationMode): string {
|
|
2608
|
-
const map: Record<ResolveIOAIManagerRecoveryAutomationMode, string> = {
|
|
2609
|
-
advance: 'current gate advances without repair after existing proof is accepted',
|
|
2610
|
-
collect_evidence: 'blockerFingerprint or evidenceHash changes with material proof, or run remains parked',
|
|
2611
|
-
read_only_diagnosis: 'diagnosis gate becomes valid or records a blocked-reproduction reason',
|
|
2612
|
-
repair_infra: 'infra preflight changes from failed/blocked to passed, or records a new infra blocker',
|
|
2613
|
-
repair_compile: 'compile/startup status changes from failed to passed, or records a new compile blocker',
|
|
2614
|
-
repair_journey_contract: 'journey contract validates with first/next/last workflow proof',
|
|
2615
|
-
repair_business_assertion: 'business assertion changes from missing/failed to passed with artifact proof',
|
|
2616
|
-
repair_release: 'hotfix/release gate changes from blocked to passed, rerun_release_gate, or parked manual',
|
|
2617
|
-
targeted_product_repair: 'owner-scoped diff is produced and deterministic proof advances to QA',
|
|
2618
|
-
manual_review: 'operator records one explicit decision',
|
|
2619
|
-
continue_gate: 'current gate records accepted proof and advances'
|
|
2620
|
-
};
|
|
2621
|
-
return map[mode] || 'runner records a validated state transition before continuing';
|
|
2622
|
-
}
|
|
2623
|
-
|
|
2624
|
-
function recoveryActionProofRequiredBeforeContinuation(mode: ResolveIOAIManagerRecoveryAutomationMode): boolean {
|
|
2625
|
-
return mode !== 'advance' && mode !== 'continue_gate' && mode !== 'manual_review';
|
|
2626
|
-
}
|
|
2627
|
-
|
|
2628
|
-
function recoveryActionExitCriteria(input: {
|
|
2629
|
-
mode: ResolveIOAIManagerRecoveryAutomationMode;
|
|
2630
|
-
dispatchAction: ResolveIOAIManagerRecoveryActionDispatchAction;
|
|
2631
|
-
requiredStateTransition: string;
|
|
2632
|
-
stopWhen: string[];
|
|
2633
|
-
}): string[] {
|
|
2634
|
-
return Array.from(new Set([
|
|
2635
|
-
`exact dispatch action ${input.dispatchAction} completed or was parked`,
|
|
2636
|
-
input.requiredStateTransition,
|
|
2637
|
-
...(recoveryActionProofRequiredBeforeContinuation(input.mode) ? ['do not continue runner until required proof artifact is attached'] : []),
|
|
2638
|
-
...input.stopWhen
|
|
2639
|
-
].map((entry) => cleanText(entry, 500)).filter(Boolean))).slice(0, 30);
|
|
2640
|
-
}
|
|
2641
|
-
|
|
2642
|
-
function recoveryReleaseHotfixCommands(policy: ResolveIOAIManagerHotfixFirstReleasePolicy | undefined): string[] {
|
|
2643
|
-
if (!policy) {
|
|
2644
|
-
return [];
|
|
2645
|
-
}
|
|
2646
|
-
return Array.from(new Set([
|
|
2647
|
-
...policy.allowedActions,
|
|
2648
|
-
...(policy.hotfixPreferred ? [
|
|
2649
|
-
'prepare_hotfix_patch_without_live_apply',
|
|
2650
|
-
'commit_and_push_hotfix_to_github',
|
|
2651
|
-
'record_github_commit_for_hotfix',
|
|
2652
|
-
'apply_live_hotfix_only_after_github_commit_proof'
|
|
2653
|
-
] : []),
|
|
2654
|
-
'run_failed_release_gate_once',
|
|
2655
|
-
'record_release_evidence'
|
|
2656
|
-
].map((entry) => cleanText(entry, 500)).filter(Boolean))).slice(0, 20);
|
|
2657
|
-
}
|
|
2658
|
-
|
|
2659
|
-
function recoveryReleaseRequiredEvidence(policy: ResolveIOAIManagerHotfixFirstReleasePolicy | undefined): string[] {
|
|
2660
|
-
if (!policy) {
|
|
2661
|
-
return [];
|
|
2662
|
-
}
|
|
2663
|
-
const stepEvidence = (policy.hotfixPlan?.steps || []).flatMap((step) => step.requiredEvidence || []);
|
|
2664
|
-
return Array.from(new Set([
|
|
2665
|
-
...policy.requiredEvidence,
|
|
2666
|
-
...stepEvidence,
|
|
2667
|
-
...(policy.hotfixPreferred ? [
|
|
2668
|
-
'full 40-character sourceCommitSha',
|
|
2669
|
-
'matching githubCommitUrl',
|
|
2670
|
-
'passed gitPushStatus before any live hotfix is treated as durable'
|
|
2671
|
-
] : [])
|
|
2672
|
-
].map((entry) => cleanText(entry, 500)).filter(Boolean))).slice(0, 30);
|
|
2673
|
-
}
|
|
2674
|
-
|
|
2675
|
-
function recoveryReleaseSuccessEvidence(policy: ResolveIOAIManagerHotfixFirstReleasePolicy | undefined): string[] {
|
|
2676
|
-
if (!policy) {
|
|
2677
|
-
return [];
|
|
2678
|
-
}
|
|
2679
|
-
const stepEvidence = (policy.hotfixPlan?.steps || []).flatMap((step) => step.successEvidence || []);
|
|
2680
|
-
return Array.from(new Set([
|
|
2681
|
-
...(policy.hotfixPlan?.acceptanceEvidence || []),
|
|
2682
|
-
...stepEvidence,
|
|
2683
|
-
...(policy.hotfixPreferred ? ['GitHub commit and push proof validates before continuation'] : [])
|
|
2684
|
-
].map((entry) => cleanText(entry, 500)).filter(Boolean))).slice(0, 30);
|
|
2685
|
-
}
|
|
2686
|
-
|
|
2687
|
-
export function buildResolveIOAIManagerRecoveryActionPacket(
|
|
2688
|
-
input: ResolveIOAIManagerRecoveryActionPacketInput
|
|
2689
|
-
): ResolveIOAIManagerRecoveryActionPacket {
|
|
2690
|
-
const checkpoint = input.checkpoint;
|
|
2691
|
-
const probe = input.probe || buildResolveIOAIManagerRecoveryEvidenceProbe({
|
|
2692
|
-
checkpoint,
|
|
2693
|
-
current: input.current,
|
|
2694
|
-
now: input.now
|
|
2695
|
-
});
|
|
2696
|
-
const mode = recoveryAutomationModeFor(checkpoint);
|
|
2697
|
-
const primaryStep = probe.steps.find((step) => step.required && step.id !== 'snapshot_current_blocker')
|
|
2698
|
-
|| probe.steps.find((step) => step.required)
|
|
2699
|
-
|| probe.steps[0];
|
|
2700
|
-
const evidenceOnly = probe.evidenceOnly || mode === 'collect_evidence' || mode === 'repair_infra' || mode === 'repair_compile' || mode === 'repair_release';
|
|
2701
|
-
const requireNewEvidence = mode === 'collect_evidence'
|
|
2702
|
-
|| checkpoint.status === 'parked'
|
|
2703
|
-
|| checkpoint.attempts >= checkpoint.maxAttemptsBeforePark;
|
|
2704
|
-
const stopWhen = Array.from(new Set([
|
|
2705
|
-
...probe.stopConditions,
|
|
2706
|
-
...(requireNewEvidence ? ['same blocker fingerprint and same evidence hash after probe'] : [])
|
|
2707
|
-
].map((entry) => cleanText(entry, 500)).filter(Boolean))).slice(0, 30);
|
|
2708
|
-
const resetLoopWhen = Array.from(new Set([
|
|
2709
|
-
...checkpoint.loopResetEvidence,
|
|
2710
|
-
...probe.loopResetEvidence,
|
|
2711
|
-
...(requireNewEvidence ? [
|
|
2712
|
-
'new material evidence: changed blocker, validated diagnosis/journey, business proof, compile/infra/release proof, or new actionable trace',
|
|
2713
|
-
'weak hash-only evidence does not reset the loop'
|
|
2714
|
-
] : [])
|
|
2715
|
-
].map((entry) => cleanText(entry, 500)).filter(Boolean))).slice(0, 30);
|
|
2716
|
-
const nextCommands = probe.steps
|
|
2717
|
-
.map((step) => cleanText(step.commandHint || `${step.kind}:${step.id}`, 500))
|
|
2718
|
-
.filter(Boolean)
|
|
2719
|
-
.slice(0, 12);
|
|
2720
|
-
const releasePolicy = mode === 'repair_release'
|
|
2721
|
-
? buildResolveIOAIManagerHotfixFirstReleasePolicy({
|
|
2722
|
-
surface: 'manager_recovery',
|
|
2723
|
-
deployStatus: checkpoint.stepType,
|
|
2724
|
-
publishStatus: checkpoint.lane
|
|
2725
|
-
})
|
|
2726
|
-
: undefined;
|
|
2727
|
-
const releaseNextCommands = recoveryReleaseHotfixCommands(releasePolicy);
|
|
2728
|
-
const releaseRequiredEvidence = recoveryReleaseRequiredEvidence(releasePolicy);
|
|
2729
|
-
const releaseSuccessEvidence = recoveryReleaseSuccessEvidence(releasePolicy);
|
|
2730
|
-
const allowedDispatchAction = dispatchActionForMode(mode);
|
|
2731
|
-
const requiredStateTransition = recoveryActionRequiredStateTransition(mode);
|
|
2732
|
-
const proofRequiredBeforeContinuation = recoveryActionProofRequiredBeforeContinuation(mode);
|
|
2733
|
-
const exitCriteria = recoveryActionExitCriteria({
|
|
2734
|
-
mode,
|
|
2735
|
-
dispatchAction: allowedDispatchAction,
|
|
2736
|
-
requiredStateTransition,
|
|
2737
|
-
stopWhen
|
|
2738
|
-
});
|
|
2739
|
-
const now = isoNow(input.now);
|
|
2740
|
-
return {
|
|
2741
|
-
actionId: stableHash('mgr-action', {
|
|
2742
|
-
checkpointId: checkpoint.checkpointId,
|
|
2743
|
-
probeId: probe.probeId,
|
|
2744
|
-
mode,
|
|
2745
|
-
evidenceHash: checkpoint.evidenceHash,
|
|
2746
|
-
blockerFingerprint: checkpoint.blockerFingerprint
|
|
2747
|
-
}),
|
|
2748
|
-
checkpointId: checkpoint.checkpointId,
|
|
2749
|
-
probeId: probe.probeId,
|
|
2750
|
-
recoveryClass: checkpoint.recoveryClass,
|
|
2751
|
-
mode,
|
|
2752
|
-
singleActionOnly: true,
|
|
2753
|
-
allowedDispatchAction,
|
|
2754
|
-
label: recoveryActionLabelFor(mode, checkpoint),
|
|
2755
|
-
lane: checkpoint.lane,
|
|
2756
|
-
stepType: checkpoint.stepType,
|
|
2757
|
-
primaryStepKind: primaryStep?.kind || 'none',
|
|
2758
|
-
objective: mode === 'collect_evidence'
|
|
2759
|
-
? probe.objective
|
|
2760
|
-
: cleanText(checkpoint.objective || probe.objective, 1000),
|
|
2761
|
-
evidenceOnly,
|
|
2762
|
-
autoRunnable: recoveryActionAutoRunnable(mode, probe),
|
|
2763
|
-
productRepairAllowed: checkpoint.productRepairAllowed && !evidenceOnly && checkpoint.status !== 'parked',
|
|
2764
|
-
expensiveModelAllowed: checkpoint.expensiveModelAllowed && checkpoint.status !== 'parked',
|
|
2765
|
-
costCeilingUsd: recoveryActionCostCeilingUsd(mode),
|
|
2766
|
-
requiredStateTransition,
|
|
2767
|
-
proofRequiredBeforeContinuation,
|
|
2768
|
-
canResetLoopAfterEvidence: resetLoopWhen.length > 0,
|
|
2769
|
-
maxAttemptsBeforePark: checkpoint.maxAttemptsBeforePark,
|
|
2770
|
-
requiredArtifacts: releasePolicy
|
|
2771
|
-
? Array.from(new Set([...probe.requiredArtifacts, ...releaseRequiredEvidence])).slice(0, 20)
|
|
2772
|
-
: probe.requiredArtifacts.slice(0, 20),
|
|
2773
|
-
nextCommands: releasePolicy
|
|
2774
|
-
? Array.from(new Set([...nextCommands, ...releaseNextCommands])).slice(0, 20)
|
|
2775
|
-
: nextCommands,
|
|
2776
|
-
successCriteria: releasePolicy
|
|
2777
|
-
? Array.from(new Set([...probe.acceptanceEvidence, ...releaseSuccessEvidence])).slice(0, 20)
|
|
2778
|
-
: probe.acceptanceEvidence.slice(0, 20),
|
|
2779
|
-
exitCriteria,
|
|
2780
|
-
retryPolicy: {
|
|
2781
|
-
allowImmediateRetry: checkpoint.status !== 'parked' && mode !== 'manual_review',
|
|
2782
|
-
requireNewEvidence,
|
|
2783
|
-
resetLoopWhen,
|
|
2784
|
-
stopWhen
|
|
2785
|
-
},
|
|
2786
|
-
...(releasePolicy ? { releasePolicy } : {}),
|
|
2787
|
-
...(mode === 'collect_evidence' ? { blockedReason: 'Manager parked this loop until the recovery action records new evidence.' } : {}),
|
|
2788
|
-
createdAt: now
|
|
2789
|
-
};
|
|
2790
|
-
}
|
|
2791
|
-
|
|
2792
|
-
function dispatchActionForMode(mode: ResolveIOAIManagerRecoveryAutomationMode): ResolveIOAIManagerRecoveryActionDispatchAction {
|
|
2793
|
-
const map: Record<ResolveIOAIManagerRecoveryAutomationMode, ResolveIOAIManagerRecoveryActionDispatchAction> = {
|
|
2794
|
-
advance: 'advance',
|
|
2795
|
-
collect_evidence: 'run_evidence_probe',
|
|
2796
|
-
read_only_diagnosis: 'run_read_only_diagnosis',
|
|
2797
|
-
repair_infra: 'run_infra_repair',
|
|
2798
|
-
repair_compile: 'run_compile_repair',
|
|
2799
|
-
repair_journey_contract: 'run_journey_contract_repair',
|
|
2800
|
-
repair_business_assertion: 'run_business_assertion_repair',
|
|
2801
|
-
repair_release: 'run_release_repair',
|
|
2802
|
-
targeted_product_repair: 'run_targeted_product_repair',
|
|
2803
|
-
manual_review: 'park_manual',
|
|
2804
|
-
continue_gate: 'continue_gate'
|
|
2805
|
-
};
|
|
2806
|
-
return map[mode] || 'park_manual';
|
|
2807
|
-
}
|
|
2808
|
-
|
|
2809
|
-
function buildRecoveryDispatchRecord(
|
|
2810
|
-
action: ResolveIOAIManagerRecoveryActionPacket,
|
|
2811
|
-
dispatchAction: ResolveIOAIManagerRecoveryActionDispatchAction,
|
|
2812
|
-
status: ResolveIOAIManagerRecoveryActionDispatchStatus,
|
|
2813
|
-
reason: string,
|
|
2814
|
-
current?: ResolveIOAIManagerFailureRecord,
|
|
2815
|
-
now?: Date | string
|
|
2816
|
-
): ResolveIOAIManagerRecoveryActionDispatchRecord {
|
|
2817
|
-
const evidenceHash = current?.evidenceHash || action.checkpointId || action.actionId;
|
|
2818
|
-
const blockerFingerprint = resolveResolveIOAIManagerBlockerFingerprint(current, action.objective);
|
|
2819
|
-
const artifactPaths = cleanList(current?.artifactPaths, 20, 500);
|
|
2820
|
-
const createdAt = isoNow(now);
|
|
2821
|
-
return {
|
|
2822
|
-
dispatchId: stableHash('mgr-dispatch', {
|
|
2823
|
-
actionId: action.actionId,
|
|
2824
|
-
dispatchAction,
|
|
2825
|
-
evidenceHash,
|
|
2826
|
-
blockerFingerprint,
|
|
2827
|
-
status
|
|
2828
|
-
}),
|
|
2829
|
-
actionId: action.actionId,
|
|
2830
|
-
checkpointId: action.checkpointId,
|
|
2831
|
-
probeId: action.probeId,
|
|
2832
|
-
mode: action.mode,
|
|
2833
|
-
dispatchAction,
|
|
2834
|
-
status,
|
|
2835
|
-
evidenceHash,
|
|
2836
|
-
blockerFingerprint,
|
|
2837
|
-
productRepairAllowed: action.productRepairAllowed && dispatchAction === 'run_targeted_product_repair',
|
|
2838
|
-
expensiveModelAllowed: action.expensiveModelAllowed && dispatchAction !== 'run_evidence_probe',
|
|
2839
|
-
reason,
|
|
2840
|
-
artifactPaths,
|
|
2841
|
-
...(status === 'started' ? { startedAt: createdAt } : {}),
|
|
2842
|
-
...(status === 'completed' ? { completedAt: createdAt } : {}),
|
|
2843
|
-
createdAt
|
|
2844
|
-
};
|
|
2845
|
-
}
|
|
2846
|
-
|
|
2847
|
-
export function decideResolveIOAIManagerRecoveryActionDispatch(
|
|
2848
|
-
input: ResolveIOAIManagerRecoveryActionDispatchInput
|
|
2849
|
-
): ResolveIOAIManagerRecoveryActionDispatchDecision {
|
|
2850
|
-
const action = input.action;
|
|
2851
|
-
const history = Array.isArray(input.history) ? input.history.filter(Boolean).slice(-50) : [];
|
|
2852
|
-
const current = input.current || {};
|
|
2853
|
-
if (!action) {
|
|
2854
|
-
return {
|
|
2855
|
-
dispatchAction: 'park_manual',
|
|
2856
|
-
allowed: false,
|
|
2857
|
-
reason: 'recovery_dispatch_missing_action',
|
|
2858
|
-
status: 'parked',
|
|
2859
|
-
canRunProductRepair: false,
|
|
2860
|
-
canRunExpensiveModel: false,
|
|
2861
|
-
shouldRecordDispatch: false,
|
|
2862
|
-
requiresNewEvidence: true,
|
|
2863
|
-
newEvidence: false
|
|
2864
|
-
};
|
|
2865
|
-
}
|
|
2866
|
-
const dispatchAction = dispatchActionForMode(action.mode);
|
|
2867
|
-
const allowedDispatchAction = action.allowedDispatchAction || dispatchAction;
|
|
2868
|
-
const evidenceHash = current.evidenceHash || action.checkpointId || action.actionId;
|
|
2869
|
-
const blockerFingerprint = resolveResolveIOAIManagerBlockerFingerprint(current, action.objective);
|
|
2870
|
-
const sameActionHistory = history.filter((entry) => entry.actionId === action.actionId);
|
|
2871
|
-
const sameEvidenceHistory = sameActionHistory.filter((entry) => entry.evidenceHash === evidenceHash
|
|
2872
|
-
&& entry.blockerFingerprint === blockerFingerprint);
|
|
2873
|
-
const activeDispatch = sameEvidenceHistory.find((entry) => entry.status === 'queued' || entry.status === 'started');
|
|
2874
|
-
const completedDispatch = sameEvidenceHistory.find((entry) => entry.status === 'completed' || entry.status === 'failed');
|
|
2875
|
-
const newEvidence = sameActionHistory.length > 0 && sameEvidenceHistory.length === 0;
|
|
2876
|
-
const requiresNewEvidence = action.retryPolicy.requireNewEvidence === true;
|
|
2877
|
-
const sameEvidenceAlreadyAttempted = !!completedDispatch && requiresNewEvidence && !newEvidence;
|
|
2878
|
-
const manualOnly = action.mode === 'manual_review' || !action.autoRunnable;
|
|
2879
|
-
const productDispatch = dispatchAction === 'run_targeted_product_repair';
|
|
2880
|
-
const expensiveDispatch = !['run_evidence_probe', 'advance', 'continue_gate', 'park_manual'].includes(dispatchAction);
|
|
2881
|
-
const evidenceAssessment = assessResolveIOAIManagerEvidenceChange(undefined, current, action.objective);
|
|
2882
|
-
const autonomyPolicy = input.autonomyPolicy
|
|
2883
|
-
? decideResolveIOAIManagerAutonomyPolicy({
|
|
2884
|
-
...(input.autonomyPolicy || {}),
|
|
2885
|
-
dispatchAction,
|
|
2886
|
-
projectedActionCostUsd: input.autonomyPolicy?.projectedActionCostUsd ?? action.costCeilingUsd,
|
|
2887
|
-
newEvidence: input.autonomyPolicy?.newEvidence ?? newEvidence,
|
|
2888
|
-
materialEvidence: input.autonomyPolicy?.materialEvidence ?? evidenceAssessment.material,
|
|
2889
|
-
evidenceStrength: input.autonomyPolicy?.evidenceStrength ?? evidenceAssessment.strength,
|
|
2890
|
-
evidenceSignals: input.autonomyPolicy?.evidenceSignals ?? evidenceAssessment.signals,
|
|
2891
|
-
sameEvidenceAlreadyAttempted: input.autonomyPolicy?.sameEvidenceAlreadyAttempted ?? sameEvidenceAlreadyAttempted,
|
|
2892
|
-
operatorApproved: input.operatorApproved
|
|
2893
|
-
})
|
|
2894
|
-
: undefined;
|
|
2895
|
-
const autonomyBlocksDispatch = !!autonomyPolicy && (
|
|
2896
|
-
autonomyPolicy.mode === 'monitor_only'
|
|
2897
|
-
|| (autonomyPolicy.requiresHumanApproval === true && input.operatorApproved !== true)
|
|
2898
|
-
|| (autonomyPolicy.canAutoDispatch !== true && input.operatorApproved !== true)
|
|
2899
|
-
|| (autonomyPolicy.blocked === true && autonomyPolicy.canManualDispatch !== true)
|
|
2900
|
-
);
|
|
2901
|
-
if (autonomyBlocksDispatch) {
|
|
2902
|
-
const record = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', `recovery_dispatch_blocked_by_autonomy_policy:${autonomyPolicy?.reason || 'unknown'}`, current, input.now);
|
|
2903
|
-
return {
|
|
2904
|
-
dispatchAction: 'park_manual',
|
|
2905
|
-
allowed: false,
|
|
2906
|
-
reason: record.reason,
|
|
2907
|
-
status: 'parked',
|
|
2908
|
-
canRunProductRepair: false,
|
|
2909
|
-
canRunExpensiveModel: false,
|
|
2910
|
-
shouldRecordDispatch: true,
|
|
2911
|
-
requiresNewEvidence,
|
|
2912
|
-
newEvidence,
|
|
2913
|
-
autonomyPolicy,
|
|
2914
|
-
dispatchRecord: record
|
|
2915
|
-
};
|
|
2916
|
-
}
|
|
2917
|
-
if (action.singleActionOnly === true && allowedDispatchAction !== dispatchAction) {
|
|
2918
|
-
const record = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', 'recovery_dispatch_action_not_in_packet_contract', current, input.now);
|
|
2919
|
-
return {
|
|
2920
|
-
dispatchAction: 'park_manual',
|
|
2921
|
-
allowed: false,
|
|
2922
|
-
reason: record.reason,
|
|
2923
|
-
status: 'parked',
|
|
2924
|
-
canRunProductRepair: false,
|
|
2925
|
-
canRunExpensiveModel: false,
|
|
2926
|
-
shouldRecordDispatch: true,
|
|
2927
|
-
requiresNewEvidence: true,
|
|
2928
|
-
newEvidence,
|
|
2929
|
-
autonomyPolicy,
|
|
2930
|
-
dispatchRecord: record
|
|
2931
|
-
};
|
|
2932
|
-
}
|
|
2933
|
-
if (manualOnly && input.operatorApproved !== true) {
|
|
2934
|
-
const record = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', 'recovery_dispatch_requires_manual_review', current, input.now);
|
|
2935
|
-
return {
|
|
2936
|
-
dispatchAction: 'park_manual',
|
|
2937
|
-
allowed: false,
|
|
2938
|
-
reason: record.reason,
|
|
2939
|
-
status: 'parked',
|
|
2940
|
-
canRunProductRepair: false,
|
|
2941
|
-
canRunExpensiveModel: false,
|
|
2942
|
-
shouldRecordDispatch: true,
|
|
2943
|
-
requiresNewEvidence,
|
|
2944
|
-
newEvidence,
|
|
2945
|
-
autonomyPolicy,
|
|
2946
|
-
dispatchRecord: record
|
|
2947
|
-
};
|
|
2948
|
-
}
|
|
2949
|
-
if (productDispatch && action.productRepairAllowed !== true) {
|
|
2950
|
-
const record = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', 'recovery_dispatch_product_repair_not_allowed', current, input.now);
|
|
2951
|
-
return {
|
|
2952
|
-
dispatchAction: 'park_manual',
|
|
2953
|
-
allowed: false,
|
|
2954
|
-
reason: record.reason,
|
|
2955
|
-
status: 'parked',
|
|
2956
|
-
canRunProductRepair: false,
|
|
2957
|
-
canRunExpensiveModel: false,
|
|
2958
|
-
shouldRecordDispatch: true,
|
|
2959
|
-
requiresNewEvidence: true,
|
|
2960
|
-
newEvidence,
|
|
2961
|
-
autonomyPolicy,
|
|
2962
|
-
dispatchRecord: record
|
|
2963
|
-
};
|
|
2964
|
-
}
|
|
2965
|
-
if (activeDispatch) {
|
|
2966
|
-
return {
|
|
2967
|
-
dispatchAction: 'park_manual',
|
|
2968
|
-
allowed: false,
|
|
2969
|
-
reason: 'recovery_dispatch_already_running_for_same_evidence',
|
|
2970
|
-
status: 'parked',
|
|
2971
|
-
canRunProductRepair: false,
|
|
2972
|
-
canRunExpensiveModel: false,
|
|
2973
|
-
shouldRecordDispatch: false,
|
|
2974
|
-
requiresNewEvidence: true,
|
|
2975
|
-
newEvidence: false,
|
|
2976
|
-
autonomyPolicy
|
|
2977
|
-
};
|
|
2978
|
-
}
|
|
2979
|
-
if (sameEvidenceAlreadyAttempted) {
|
|
2980
|
-
const record = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', 'recovery_dispatch_same_evidence_already_attempted', current, input.now);
|
|
2981
|
-
return {
|
|
2982
|
-
dispatchAction: 'park_manual',
|
|
2983
|
-
allowed: false,
|
|
2984
|
-
reason: record.reason,
|
|
2985
|
-
status: 'parked',
|
|
2986
|
-
canRunProductRepair: false,
|
|
2987
|
-
canRunExpensiveModel: false,
|
|
2988
|
-
shouldRecordDispatch: true,
|
|
2989
|
-
requiresNewEvidence: true,
|
|
2990
|
-
newEvidence: false,
|
|
2991
|
-
autonomyPolicy,
|
|
2992
|
-
dispatchRecord: record
|
|
2993
|
-
};
|
|
2994
|
-
}
|
|
2995
|
-
const status: ResolveIOAIManagerRecoveryActionDispatchStatus = dispatchAction === 'advance' || dispatchAction === 'continue_gate'
|
|
2996
|
-
? 'completed'
|
|
2997
|
-
: 'started';
|
|
2998
|
-
const record = buildRecoveryDispatchRecord(action, dispatchAction, status, 'recovery_dispatch_allowed', current, input.now);
|
|
2999
|
-
return {
|
|
3000
|
-
dispatchAction,
|
|
3001
|
-
allowed: true,
|
|
3002
|
-
reason: 'recovery_dispatch_allowed',
|
|
3003
|
-
status,
|
|
3004
|
-
canRunProductRepair: productDispatch && action.productRepairAllowed === true,
|
|
3005
|
-
canRunExpensiveModel: expensiveDispatch && action.expensiveModelAllowed === true,
|
|
3006
|
-
shouldRecordDispatch: true,
|
|
3007
|
-
requiresNewEvidence,
|
|
3008
|
-
newEvidence,
|
|
3009
|
-
autonomyPolicy,
|
|
3010
|
-
dispatchRecord: record
|
|
3011
|
-
};
|
|
3012
|
-
}
|
|
3013
|
-
|
|
3014
|
-
export function appendResolveIOAIManagerRecoveryActionDispatch(
|
|
3015
|
-
history: ResolveIOAIManagerRecoveryActionDispatchRecord[] | undefined,
|
|
3016
|
-
record: ResolveIOAIManagerRecoveryActionDispatchRecord | undefined,
|
|
3017
|
-
limit = 50
|
|
3018
|
-
): ResolveIOAIManagerRecoveryActionDispatchRecord[] {
|
|
3019
|
-
const existing = Array.isArray(history) ? history.filter(Boolean) : [];
|
|
3020
|
-
if (!record) {
|
|
3021
|
-
return existing.slice(-limit);
|
|
3022
|
-
}
|
|
3023
|
-
const deduped = existing.filter((entry) => entry.dispatchId !== record.dispatchId);
|
|
3024
|
-
return [...deduped, record].slice(-limit);
|
|
3025
|
-
}
|
|
3026
|
-
|
|
3027
|
-
function recoveryExecutionPhaseForDispatch(
|
|
3028
|
-
dispatchAction: ResolveIOAIManagerRecoveryActionDispatchAction
|
|
3029
|
-
): ResolveIOAIManagerRecoveryExecutionPhase {
|
|
3030
|
-
const map: Record<ResolveIOAIManagerRecoveryActionDispatchAction, ResolveIOAIManagerRecoveryExecutionPhase> = {
|
|
3031
|
-
run_evidence_probe: 'evidence',
|
|
3032
|
-
run_read_only_diagnosis: 'diagnosis',
|
|
3033
|
-
run_infra_repair: 'infra',
|
|
3034
|
-
run_compile_repair: 'compile',
|
|
3035
|
-
run_journey_contract_repair: 'journey',
|
|
3036
|
-
run_business_assertion_repair: 'business_proof',
|
|
3037
|
-
run_release_repair: 'release',
|
|
3038
|
-
run_targeted_product_repair: 'product_repair',
|
|
3039
|
-
advance: 'advance',
|
|
3040
|
-
continue_gate: 'advance',
|
|
3041
|
-
park_manual: 'manual'
|
|
3042
|
-
};
|
|
3043
|
-
return map[dispatchAction] || 'manual';
|
|
3044
|
-
}
|
|
3045
|
-
|
|
3046
|
-
function recoveryDirectiveReason(
|
|
3047
|
-
action: ResolveIOAIManagerRecoveryActionPacket | undefined,
|
|
3048
|
-
decision: ResolveIOAIManagerRecoveryActionDispatchDecision,
|
|
3049
|
-
current?: ResolveIOAIManagerFailureRecord
|
|
3050
|
-
): string {
|
|
3051
|
-
const blocker = cleanText(current?.blocker || current?.summary || '', 260);
|
|
3052
|
-
const label = cleanText(action?.label, 120) || decision.dispatchAction;
|
|
3053
|
-
const objective = cleanText(action?.objective, 260);
|
|
3054
|
-
return [
|
|
3055
|
-
`MANAGER_RECOVERY:${decision.dispatchAction}`,
|
|
3056
|
-
`label=${label}`,
|
|
3057
|
-
decision.reason ? `reason=${decision.reason}` : '',
|
|
3058
|
-
blocker ? `blocker=${blocker}` : '',
|
|
3059
|
-
objective ? `objective=${objective}` : ''
|
|
3060
|
-
].filter(Boolean).join(' | ');
|
|
3061
|
-
}
|
|
3062
|
-
|
|
3063
|
-
export function buildResolveIOAIManagerRecoveryExecutionDirective(
|
|
3064
|
-
input: ResolveIOAIManagerRecoveryExecutionDirectiveInput
|
|
3065
|
-
): ResolveIOAIManagerRecoveryExecutionDirective {
|
|
3066
|
-
const action = input.action;
|
|
3067
|
-
const dispatchDecision = input.dispatchDecision || decideResolveIOAIManagerRecoveryActionDispatch({
|
|
3068
|
-
action,
|
|
3069
|
-
current: input.current,
|
|
3070
|
-
autonomyPolicy: input.autonomyPolicy,
|
|
3071
|
-
now: input.now
|
|
3072
|
-
});
|
|
3073
|
-
const dispatchAction = dispatchDecision.dispatchAction;
|
|
3074
|
-
const now = isoNow(input.now);
|
|
3075
|
-
const phase = recoveryExecutionPhaseForDispatch(dispatchAction);
|
|
3076
|
-
const lane = cleanText(action?.lane || input.current?.lane || '', 120);
|
|
3077
|
-
const stepType = cleanText(action?.stepType || input.current?.stepType || '', 120);
|
|
3078
|
-
const rerunReason = recoveryDirectiveReason(action, dispatchDecision, input.current);
|
|
3079
|
-
const surface = cleanText(input.surface || 'runner', 120);
|
|
3080
|
-
return {
|
|
3081
|
-
directiveId: stableHash('mgr-directive', {
|
|
3082
|
-
surface,
|
|
3083
|
-
actionId: action?.actionId || '',
|
|
3084
|
-
dispatchAction,
|
|
3085
|
-
reason: dispatchDecision.reason,
|
|
3086
|
-
evidenceHash: dispatchDecision.dispatchRecord?.evidenceHash || input.current?.evidenceHash || ''
|
|
3087
|
-
}),
|
|
3088
|
-
surface,
|
|
3089
|
-
dispatchAction,
|
|
3090
|
-
phase,
|
|
3091
|
-
allowed: dispatchDecision.allowed,
|
|
3092
|
-
status: dispatchDecision.status,
|
|
3093
|
-
reason: dispatchDecision.reason,
|
|
3094
|
-
singleActionOnly: action?.singleActionOnly !== false,
|
|
3095
|
-
allowedDispatchAction: action?.allowedDispatchAction || dispatchAction,
|
|
3096
|
-
lane,
|
|
3097
|
-
stepType,
|
|
3098
|
-
nextActionLabel: cleanText(action?.label, 160) || dispatchAction,
|
|
3099
|
-
rerunReason,
|
|
3100
|
-
evidenceOnly: action?.evidenceOnly !== false || dispatchAction === 'run_evidence_probe',
|
|
3101
|
-
autoRunnable: action?.autoRunnable === true,
|
|
3102
|
-
productRepairAllowed: action?.productRepairAllowed === true,
|
|
3103
|
-
expensiveModelAllowed: action?.expensiveModelAllowed === true,
|
|
3104
|
-
costCeilingUsd: Math.max(0, Number(action?.costCeilingUsd || 0) || 0),
|
|
3105
|
-
requiredStateTransition: cleanText(action?.requiredStateTransition, 500),
|
|
3106
|
-
proofRequiredBeforeContinuation: action?.proofRequiredBeforeContinuation === true,
|
|
3107
|
-
canRunProductRepair: dispatchDecision.canRunProductRepair,
|
|
3108
|
-
canRunExpensiveModel: dispatchDecision.canRunExpensiveModel,
|
|
3109
|
-
canResetLoopAfterEvidence: action?.canResetLoopAfterEvidence === true,
|
|
3110
|
-
requiresNewEvidence: dispatchDecision.requiresNewEvidence,
|
|
3111
|
-
newEvidence: dispatchDecision.newEvidence,
|
|
3112
|
-
...(dispatchDecision.autonomyPolicy ? { autonomyPolicy: dispatchDecision.autonomyPolicy } : {}),
|
|
3113
|
-
maxAttemptsBeforePark: Math.max(1, Number(action?.maxAttemptsBeforePark || 1) || 1),
|
|
3114
|
-
requiredArtifacts: cleanList(action?.requiredArtifacts, 20, 500),
|
|
3115
|
-
nextCommands: cleanList(action?.nextCommands, 20, 500),
|
|
3116
|
-
successCriteria: cleanList(action?.successCriteria, 20, 500),
|
|
3117
|
-
exitCriteria: cleanList(action?.exitCriteria, 30, 500),
|
|
3118
|
-
forbiddenActions: action?.productRepairAllowed === true
|
|
3119
|
-
? []
|
|
3120
|
-
: Array.from(new Set([
|
|
3121
|
-
'Do not run product-code repair from this directive unless canRunProductRepair is true.',
|
|
3122
|
-
...(action?.releasePolicy?.forbiddenActions || [])
|
|
3123
|
-
])).slice(0, 20),
|
|
3124
|
-
...(dispatchDecision.dispatchRecord ? { dispatchRecord: dispatchDecision.dispatchRecord } : {}),
|
|
3125
|
-
...(action?.releasePolicy ? { releasePolicy: action.releasePolicy } : {}),
|
|
3126
|
-
createdAt: now
|
|
3127
|
-
};
|
|
3128
|
-
}
|
|
3129
|
-
|
|
3130
|
-
function newListEntries(current: string[], previous: string[]): string[] {
|
|
3131
|
-
const existing = new Set(previous.map((entry) => cleanText(entry, 500)).filter(Boolean));
|
|
3132
|
-
return current
|
|
3133
|
-
.map((entry) => cleanText(entry, 500))
|
|
3134
|
-
.filter((entry) => entry && !existing.has(entry));
|
|
3135
|
-
}
|
|
3136
|
-
|
|
3137
|
-
function recordLane(value: any): string {
|
|
3138
|
-
return cleanText(value?.lane, 80);
|
|
3139
|
-
}
|
|
3140
|
-
|
|
3141
|
-
function recordStepType(value: any): string {
|
|
3142
|
-
return cleanText(value?.stepType, 80);
|
|
3143
|
-
}
|
|
3144
|
-
|
|
3145
|
-
function recordFailureClass(value: any): string {
|
|
3146
|
-
const valueText = cleanText(value?.failureClass, 80);
|
|
3147
|
-
return valueText ? normalizeResolveIOAIManagerFailureClass(valueText) : '';
|
|
3148
|
-
}
|
|
3149
|
-
|
|
3150
|
-
function materialEvidenceText(record: ResolveIOAIManagerFailureRecord | undefined, paths: string[] = []): string {
|
|
3151
|
-
return [
|
|
3152
|
-
cleanText(record?.blocker, 1200),
|
|
3153
|
-
cleanText(record?.summary, 1200),
|
|
3154
|
-
...paths.map((path) => cleanText(path, 500))
|
|
3155
|
-
].filter(Boolean).join(' ').toLowerCase();
|
|
3156
|
-
}
|
|
3157
|
-
|
|
3158
|
-
function hasMaterialEvidenceLanguage(text: string): boolean {
|
|
3159
|
-
return /(business|assertion|proof|before|after|dom|trace|stack|network|mongo|query|diff|scope|diagnosis|journey|contract|workflow|compile|build|preflight|infra|chrome|puppeteer|release|publish|deploy|sample|seed|route|auth|hydration|performance|slow|owner|root.?cause|repro|pdf|export|upload|import|filter|invoice|saved|calculated|comparison|report)/i.test(text);
|
|
3160
|
-
}
|
|
3161
|
-
|
|
3162
|
-
export function assessResolveIOAIManagerEvidenceChange(
|
|
3163
|
-
previous: (ResolveIOAIManagerFailureRecord | ResolveIOAIManagerRecoveryCheckpoint | undefined),
|
|
3164
|
-
current: ResolveIOAIManagerFailureRecord | undefined,
|
|
3165
|
-
fallbackObjective?: string
|
|
3166
|
-
): ResolveIOAIManagerEvidenceAssessment {
|
|
3167
|
-
const currentRecord = current || {};
|
|
3168
|
-
const previousRecord = previous as any;
|
|
3169
|
-
const previousEvidenceHash = previousRecord ? cleanText(previousRecord.evidenceHash, 160) || hashResolveIOAIManagerEvidence(previousRecord) : '';
|
|
3170
|
-
const currentEvidenceHash = hashResolveIOAIManagerEvidence(currentRecord);
|
|
3171
|
-
const previousBlockerFingerprint = previousRecord
|
|
3172
|
-
? cleanText(previousRecord.blockerFingerprint, 160) || resolveResolveIOAIManagerBlockerFingerprint(previousRecord, fallbackObjective)
|
|
3173
|
-
: '';
|
|
3174
|
-
const currentBlockerFingerprint = resolveResolveIOAIManagerBlockerFingerprint(currentRecord, fallbackObjective);
|
|
3175
|
-
const previousChangedFiles = cleanList(previousRecord?.changedFiles, 80, 500);
|
|
3176
|
-
const currentChangedFiles = cleanList(currentRecord.changedFiles, 80, 500);
|
|
3177
|
-
const previousArtifactPaths = cleanList(previousRecord?.artifactPaths, 80, 500);
|
|
3178
|
-
const currentArtifactPaths = cleanList(currentRecord.artifactPaths, 80, 500);
|
|
3179
|
-
const addedChangedFiles = newListEntries(currentChangedFiles, previousChangedFiles);
|
|
3180
|
-
const addedArtifactPaths = newListEntries(currentArtifactPaths, previousArtifactPaths);
|
|
3181
|
-
const signals: string[] = [];
|
|
3182
|
-
if (previousEvidenceHash && currentEvidenceHash !== previousEvidenceHash) {
|
|
3183
|
-
signals.push('evidence_hash_changed');
|
|
3184
|
-
}
|
|
3185
|
-
if (previousBlockerFingerprint && currentBlockerFingerprint !== previousBlockerFingerprint) {
|
|
3186
|
-
signals.push('blocker_fingerprint_changed');
|
|
3187
|
-
}
|
|
3188
|
-
if (recordLane(previousRecord) && recordLane(previousRecord) !== recordLane(currentRecord)) {
|
|
3189
|
-
signals.push('lane_changed');
|
|
3190
|
-
}
|
|
3191
|
-
if (recordStepType(previousRecord) && recordStepType(previousRecord) !== recordStepType(currentRecord)) {
|
|
3192
|
-
signals.push('step_type_changed');
|
|
3193
|
-
}
|
|
3194
|
-
if (recordFailureClass(previousRecord) && recordFailureClass(previousRecord) !== recordFailureClass(currentRecord)) {
|
|
3195
|
-
signals.push('failure_class_changed');
|
|
3196
|
-
}
|
|
3197
|
-
if (addedChangedFiles.length) {
|
|
3198
|
-
signals.push('changed_files_added');
|
|
3199
|
-
}
|
|
3200
|
-
if (addedArtifactPaths.length) {
|
|
3201
|
-
signals.push('artifact_paths_added');
|
|
3202
|
-
}
|
|
3203
|
-
if (isPassingOutcome(currentRecord)) {
|
|
3204
|
-
signals.push('passing_outcome');
|
|
3205
|
-
}
|
|
3206
|
-
const changed = signals.length > 0;
|
|
3207
|
-
const evidenceText = materialEvidenceText(currentRecord, addedArtifactPaths);
|
|
3208
|
-
const pathOrSummaryHasMaterialSignal = hasMaterialEvidenceLanguage(evidenceText);
|
|
3209
|
-
const proof = signals.includes('passing_outcome');
|
|
3210
|
-
const structuralChange = signals.some((signal) => /blocker_fingerprint_changed|lane_changed|step_type_changed|failure_class_changed/.test(signal));
|
|
3211
|
-
const materialArtifact = addedArtifactPaths.length > 0 && pathOrSummaryHasMaterialSignal;
|
|
3212
|
-
const materialChangedFiles = addedChangedFiles.length > 0
|
|
3213
|
-
&& /diagnosis|owner|scope|diff|root.?cause|failing path|stack|trace|business|proof|compile|release|deploy|workflow|journey/i.test(evidenceText);
|
|
3214
|
-
const material = proof || structuralChange || materialArtifact || materialChangedFiles;
|
|
3215
|
-
const strength: ResolveIOAIManagerEvidenceStrength = proof
|
|
3216
|
-
? 'proof'
|
|
3217
|
-
: material
|
|
3218
|
-
? 'material'
|
|
3219
|
-
: changed
|
|
3220
|
-
? 'weak'
|
|
3221
|
-
: 'none';
|
|
3222
|
-
return {
|
|
3223
|
-
changed,
|
|
3224
|
-
material,
|
|
3225
|
-
strength,
|
|
3226
|
-
signals,
|
|
3227
|
-
evidenceHash: currentEvidenceHash,
|
|
3228
|
-
blockerFingerprint: currentBlockerFingerprint
|
|
3229
|
-
};
|
|
3230
|
-
}
|
|
3231
|
-
|
|
3232
|
-
function proposedActionIsProductRepair(value: any): boolean {
|
|
3233
|
-
const normalized = cleanText(value, 120);
|
|
3234
|
-
return /product|code|repair|build|business|journey|workflow/i.test(normalized)
|
|
3235
|
-
&& !/infra|compile|diagnosis|manual|evidence|release|publish/i.test(normalized);
|
|
3236
|
-
}
|
|
3237
|
-
|
|
3238
|
-
export function decideResolveIOAIManagerRecoveryGate(
|
|
3239
|
-
input: ResolveIOAIManagerRecoveryGateInput
|
|
3240
|
-
): ResolveIOAIManagerRecoveryGateDecision {
|
|
3241
|
-
const checkpoint = input.checkpoint;
|
|
3242
|
-
const hasCurrent = !!input.current;
|
|
3243
|
-
const current = input.current || {};
|
|
3244
|
-
const assessment = hasCurrent
|
|
3245
|
-
? assessResolveIOAIManagerEvidenceChange(checkpoint, current, checkpoint.objective)
|
|
3246
|
-
: {
|
|
3247
|
-
changed: false,
|
|
3248
|
-
material: false,
|
|
3249
|
-
strength: 'none' as ResolveIOAIManagerEvidenceStrength,
|
|
3250
|
-
signals: [],
|
|
3251
|
-
evidenceHash: checkpoint.evidenceHash,
|
|
3252
|
-
blockerFingerprint: checkpoint.blockerFingerprint
|
|
3253
|
-
};
|
|
3254
|
-
const blockerFingerprint = assessment.blockerFingerprint;
|
|
3255
|
-
const evidenceHash = assessment.evidenceHash;
|
|
3256
|
-
const changedFiles = hasCurrent ? cleanList(current.changedFiles, 80, 500) : checkpoint.changedFiles;
|
|
3257
|
-
const artifactPaths = hasCurrent ? cleanList(current.artifactPaths, 80, 500) : checkpoint.artifactPaths;
|
|
3258
|
-
const evidenceChanged = assessment.changed;
|
|
3259
|
-
const proposedAction = cleanText(input.proposedAction || checkpoint.allowedAction, 120);
|
|
3260
|
-
const productRepairRequested = proposedActionIsProductRepair(proposedAction);
|
|
3261
|
-
const missingEvidence = checkpoint.requiredEvidence.filter((required) => {
|
|
3262
|
-
const normalized = cleanText(required, 120).toLowerCase();
|
|
3263
|
-
if (!normalized) {
|
|
3264
|
-
return false;
|
|
3265
|
-
}
|
|
3266
|
-
if (/artifact|log|proof|gate|evidence/.test(normalized)) {
|
|
3267
|
-
return !artifactPaths.length && !current.evidenceHash;
|
|
3268
|
-
}
|
|
3269
|
-
if (/diff|changed files|owner|scope|file/.test(normalized)) {
|
|
3270
|
-
return !changedFiles.length;
|
|
3271
|
-
}
|
|
3272
|
-
return false;
|
|
3273
|
-
});
|
|
3274
|
-
const makeDecision = (
|
|
3275
|
-
action: ResolveIOAIManagerRecoveryGateAction,
|
|
3276
|
-
reason: string,
|
|
3277
|
-
overrides: Partial<ResolveIOAIManagerRecoveryGateDecision> = {}
|
|
3278
|
-
): ResolveIOAIManagerRecoveryGateDecision => ({
|
|
3279
|
-
action,
|
|
3280
|
-
reason,
|
|
3281
|
-
canRunProductRepair: checkpoint.productRepairAllowed,
|
|
3282
|
-
canRunExpensiveModel: checkpoint.expensiveModelAllowed,
|
|
3283
|
-
shouldResetLoopBudget: false,
|
|
3284
|
-
shouldIncrementAttempt: action === 'allow',
|
|
3285
|
-
newEvidence: evidenceChanged,
|
|
3286
|
-
materialEvidence: assessment.material,
|
|
3287
|
-
evidenceStrength: assessment.strength,
|
|
3288
|
-
evidenceSignals: assessment.signals,
|
|
3289
|
-
blockerFingerprint,
|
|
3290
|
-
evidenceHash,
|
|
3291
|
-
missingEvidence,
|
|
3292
|
-
checkpoint: {
|
|
3293
|
-
...checkpoint,
|
|
3294
|
-
updatedAt: isoNow(input.now)
|
|
3295
|
-
},
|
|
3296
|
-
...overrides
|
|
3297
|
-
});
|
|
3298
|
-
|
|
3299
|
-
if (checkpoint.status === 'complete') {
|
|
3300
|
-
return makeDecision('complete', 'recovery_gate_checkpoint_already_complete', {
|
|
3301
|
-
canRunProductRepair: false,
|
|
3302
|
-
canRunExpensiveModel: false,
|
|
3303
|
-
shouldIncrementAttempt: false
|
|
3304
|
-
});
|
|
3305
|
-
}
|
|
3306
|
-
if (checkpoint.status === 'manual_handoff' && input.operatorApproved !== true) {
|
|
3307
|
-
return makeDecision('manual_handoff', 'recovery_gate_manual_handoff_requires_operator_approval', {
|
|
3308
|
-
canRunProductRepair: false,
|
|
3309
|
-
canRunExpensiveModel: false,
|
|
3310
|
-
shouldIncrementAttempt: false
|
|
3311
|
-
});
|
|
3312
|
-
}
|
|
3313
|
-
if (productRepairRequested && !checkpoint.productRepairAllowed) {
|
|
3314
|
-
return makeDecision('reject_action', 'recovery_gate_product_repair_not_allowed_for_checkpoint', {
|
|
3315
|
-
canRunProductRepair: false,
|
|
3316
|
-
canRunExpensiveModel: false,
|
|
3317
|
-
shouldIncrementAttempt: false
|
|
3318
|
-
});
|
|
3319
|
-
}
|
|
3320
|
-
if (checkpoint.status === 'parked' && !evidenceChanged) {
|
|
3321
|
-
return makeDecision('collect_new_evidence', 'recovery_gate_parked_until_new_evidence', {
|
|
3322
|
-
canRunProductRepair: false,
|
|
3323
|
-
canRunExpensiveModel: false,
|
|
3324
|
-
shouldIncrementAttempt: false
|
|
3325
|
-
});
|
|
3326
|
-
}
|
|
3327
|
-
if (checkpoint.status === 'parked' && evidenceChanged && !assessment.material) {
|
|
3328
|
-
return makeDecision('collect_new_evidence', 'recovery_gate_weak_evidence_requires_material_signal', {
|
|
3329
|
-
canRunProductRepair: false,
|
|
3330
|
-
canRunExpensiveModel: false,
|
|
3331
|
-
shouldIncrementAttempt: false
|
|
3332
|
-
});
|
|
3333
|
-
}
|
|
3334
|
-
if (checkpoint.attempts >= checkpoint.maxAttemptsBeforePark && !evidenceChanged) {
|
|
3335
|
-
return makeDecision('collect_new_evidence', 'recovery_gate_attempt_limit_requires_new_evidence', {
|
|
3336
|
-
canRunProductRepair: false,
|
|
3337
|
-
canRunExpensiveModel: false,
|
|
3338
|
-
shouldIncrementAttempt: false
|
|
3339
|
-
});
|
|
3340
|
-
}
|
|
3341
|
-
if (checkpoint.attempts >= checkpoint.maxAttemptsBeforePark && evidenceChanged && !assessment.material) {
|
|
3342
|
-
return makeDecision('collect_new_evidence', 'recovery_gate_attempt_limit_requires_material_evidence', {
|
|
3343
|
-
canRunProductRepair: false,
|
|
3344
|
-
canRunExpensiveModel: false,
|
|
3345
|
-
shouldIncrementAttempt: false
|
|
3346
|
-
});
|
|
3347
|
-
}
|
|
3348
|
-
if (evidenceChanged) {
|
|
3349
|
-
const currentFailureClass = normalizeResolveIOAIManagerFailureClass(current.failureClass);
|
|
3350
|
-
const currentCanRunProductRepair = checkpoint.productRepairAllowed
|
|
3351
|
-
|| !/^(infra|compile|diagnosis|release)$/i.test(currentFailureClass);
|
|
3352
|
-
return makeDecision('allow', 'recovery_gate_new_material_evidence_unblocks_retry', {
|
|
3353
|
-
canRunProductRepair: currentCanRunProductRepair,
|
|
3354
|
-
canRunExpensiveModel: checkpoint.expensiveModelAllowed || currentCanRunProductRepair,
|
|
3355
|
-
shouldResetLoopBudget: true
|
|
3356
|
-
});
|
|
3357
|
-
}
|
|
3358
|
-
return makeDecision('allow', 'recovery_gate_action_allowed');
|
|
3359
|
-
}
|
|
3360
|
-
|
|
3361
|
-
function failureKey(record: ResolveIOAIManagerFailureRecord | undefined): string {
|
|
3362
|
-
if (!record) {
|
|
3363
|
-
return '';
|
|
3364
|
-
}
|
|
3365
|
-
const failureClass = normalizeResolveIOAIManagerFailureClass(record.failureClass);
|
|
3366
|
-
const blockerFingerprint = resolveResolveIOAIManagerBlockerFingerprint(record);
|
|
3367
|
-
return `${cleanText(record.lane, 80)}:${cleanText(record.stepType, 80)}:${failureClass}:${blockerFingerprint}`;
|
|
3368
|
-
}
|
|
3369
|
-
|
|
3370
|
-
function failureEvidenceKey(record: ResolveIOAIManagerFailureRecord | undefined): string {
|
|
3371
|
-
return `${failureKey(record)}:${hashResolveIOAIManagerEvidence(record)}`;
|
|
3372
|
-
}
|
|
3373
|
-
|
|
3374
|
-
function isPassingOutcome(record: ResolveIOAIManagerFailureRecord | undefined): boolean {
|
|
3375
|
-
return /^(pass|accepted|ready_to_publish|published|ready_for_merge|complete|completed)$/i.test(cleanText(record?.outcome, 80));
|
|
3376
|
-
}
|
|
3377
|
-
|
|
3378
|
-
function isManualOutcome(record: ResolveIOAIManagerFailureRecord | undefined): boolean {
|
|
3379
|
-
return /^(manual_handoff|park_manual|stopped)$/i.test(cleanText(record?.outcome, 80));
|
|
3380
|
-
}
|
|
3381
|
-
|
|
3382
|
-
export function buildResolveIOAIManagerRecoveryPlan(input: ResolveIOAIManagerRecoveryPlanInput = {}): ResolveIOAIManagerRecoveryPlan {
|
|
3383
|
-
const action = cleanText(input.action, 80) || 'continue';
|
|
3384
|
-
const reason = cleanText(input.reason, 240);
|
|
3385
|
-
const failureClass = normalizeResolveIOAIManagerFailureClass(input.failureClass);
|
|
3386
|
-
const lane = cleanText(input.lane, 80) || 'supervisor';
|
|
3387
|
-
const stepType = cleanText(input.stepType, 80) || 'unknown';
|
|
3388
|
-
const blocker = cleanText(input.blocker, 700);
|
|
3389
|
-
const changedFiles = cleanList(input.changedFiles, 12, 240);
|
|
3390
|
-
const artifactPaths = cleanList(input.artifactPaths, 12, 240);
|
|
3391
|
-
const maxAttemptsBeforePark = Math.max(1, Number(input.maxSameFailureRepeats || 3) || 3);
|
|
3392
|
-
const commonNotes = [
|
|
3393
|
-
reason ? `policy_reason=${reason}` : '',
|
|
3394
|
-
blocker ? `blocker=${blocker}` : '',
|
|
3395
|
-
changedFiles.length ? `changed_files=${changedFiles.join(', ')}` : '',
|
|
3396
|
-
artifactPaths.length ? `artifacts=${artifactPaths.join(', ')}` : ''
|
|
3397
|
-
].filter(Boolean);
|
|
3398
|
-
const base: Omit<ResolveIOAIManagerRecoveryPlan, 'recoveryClass' | 'nextActionLabel' | 'objective' | 'allowedAction' | 'productRepairAllowed' | 'expensiveModelAllowed' | 'finiteSteps' | 'requiredEvidence' | 'loopResetEvidence' | 'forbiddenActions'> = {
|
|
3399
|
-
lane,
|
|
3400
|
-
stepType,
|
|
3401
|
-
maxAttemptsBeforePark,
|
|
3402
|
-
notes: commonNotes
|
|
3403
|
-
};
|
|
3404
|
-
const makePlan = (
|
|
3405
|
-
recoveryClass: ResolveIOAIManagerRecoveryClass,
|
|
3406
|
-
nextActionLabel: string,
|
|
3407
|
-
objective: string,
|
|
3408
|
-
allowedAction: string,
|
|
3409
|
-
productRepairAllowed: boolean,
|
|
3410
|
-
expensiveModelAllowed: boolean,
|
|
3411
|
-
finiteSteps: string[],
|
|
3412
|
-
requiredEvidence: string[],
|
|
3413
|
-
loopResetEvidence: string[],
|
|
3414
|
-
forbiddenActions: string[]
|
|
3415
|
-
): ResolveIOAIManagerRecoveryPlan => ({
|
|
3416
|
-
...base,
|
|
3417
|
-
recoveryClass,
|
|
3418
|
-
nextActionLabel,
|
|
3419
|
-
objective,
|
|
3420
|
-
allowedAction,
|
|
3421
|
-
productRepairAllowed,
|
|
3422
|
-
expensiveModelAllowed,
|
|
3423
|
-
finiteSteps,
|
|
3424
|
-
requiredEvidence,
|
|
3425
|
-
loopResetEvidence,
|
|
3426
|
-
forbiddenActions
|
|
3427
|
-
});
|
|
3428
|
-
if (action === 'reset_loop_budget') {
|
|
3429
|
-
return makePlan(
|
|
3430
|
-
'advance_after_proof',
|
|
3431
|
-
'Advance After Proof',
|
|
3432
|
-
'Checkpoint the passing evidence and move to the next required gate.',
|
|
3433
|
-
'advance_to_next_gate',
|
|
3434
|
-
false,
|
|
3435
|
-
false,
|
|
3436
|
-
[
|
|
3437
|
-
'Record the passing artifact, changed files, and gate result.',
|
|
3438
|
-
'Reset the loop counter because a real gate passed.',
|
|
3439
|
-
'Select the next unmet deterministic gate instead of repeating the previous repair.'
|
|
3440
|
-
],
|
|
3441
|
-
['passing gate record', 'artifact or log proving the passed check'],
|
|
3442
|
-
['new passing gate result', 'new business proof artifact'],
|
|
3443
|
-
['rerun the same repair after a pass', 'mark accepted without the required downstream gate']
|
|
3444
|
-
);
|
|
3445
|
-
}
|
|
3446
|
-
if (action === 'manual_handoff') {
|
|
3447
|
-
return makePlan(
|
|
3448
|
-
'manual_handoff',
|
|
3449
|
-
'Manual Review',
|
|
3450
|
-
'Stop autonomous repair and expose the blocker, evidence, and last safe state.',
|
|
3451
|
-
'manual_review_only',
|
|
3452
|
-
false,
|
|
3453
|
-
false,
|
|
3454
|
-
[
|
|
3455
|
-
'Freeze further product-code edits.',
|
|
3456
|
-
'Show the blocker fingerprint, evidence hash, and prior attempts.',
|
|
3457
|
-
'Require a human edit to classification, scope, or retry policy before another model/code loop.'
|
|
3458
|
-
],
|
|
3459
|
-
['blocker fingerprint', 'evidence hash', 'attempt history'],
|
|
3460
|
-
['manual policy change', 'new external evidence'],
|
|
3461
|
-
['start another automatic model repair', 'hide the failed evidence']
|
|
3462
|
-
);
|
|
3463
|
-
}
|
|
3464
|
-
if (action === 'budget_stop') {
|
|
3465
|
-
return makePlan(
|
|
3466
|
-
'manual_handoff',
|
|
3467
|
-
'Budget Stop Review',
|
|
3468
|
-
'Stop autonomous model/code loops until a cheaper scoped next action is selected.',
|
|
3469
|
-
'budget_review_only',
|
|
3470
|
-
false,
|
|
3471
|
-
false,
|
|
3472
|
-
[
|
|
3473
|
-
'Freeze further model repair loops.',
|
|
3474
|
-
'Show the prompt/token/runtime budget that tripped the guard.',
|
|
3475
|
-
'Select a cheaper deterministic gate, infra repair, diagnosis revision, or manual scope edit before resuming.'
|
|
3476
|
-
],
|
|
3477
|
-
['budget guard reason', 'latest prompt estimate', 'last blocker/evidence hash'],
|
|
3478
|
-
['manual scope reduction', 'new deterministic evidence', 'approved budget policy change'],
|
|
3479
|
-
['start another broad model prompt', 'reset budget without new evidence', 'hide cost context']
|
|
3480
|
-
);
|
|
3481
|
-
}
|
|
3482
|
-
if (failureClass === 'diagnosis' || stepType === 'diagnosis_gate') {
|
|
3483
|
-
return makePlan(
|
|
3484
|
-
'diagnosis_only',
|
|
3485
|
-
'Run Diagnosis Gate',
|
|
3486
|
-
'Produce a falsifiable root-cause diagnosis before any repair.',
|
|
3487
|
-
'read_only_diagnosis',
|
|
3488
|
-
false,
|
|
3489
|
-
true,
|
|
3490
|
-
[
|
|
3491
|
-
'Reproduce or explicitly classify the customer issue.',
|
|
3492
|
-
'Accept one falsifiable hypothesis and record rejected alternatives.',
|
|
3493
|
-
'Identify the frontend/backend/shared failing path.',
|
|
3494
|
-
'Select a small owner_files set.',
|
|
3495
|
-
'Define exact before/action/after business proof.'
|
|
3496
|
-
],
|
|
3497
|
-
['issue_case', 'accepted_hypothesis', 'rejected_alternatives', 'failing_path', 'owner_files', 'proof_plan'],
|
|
3498
|
-
['new reproduction artifact', 'new query/log/code evidence', 'validated diagnosis gate'],
|
|
3499
|
-
['edit product code', 'run broad repo repair', 'accept route-load evidence as success']
|
|
3500
|
-
);
|
|
3501
|
-
}
|
|
3502
|
-
if (failureClass === 'owner_scope' || failureClass === 'out_of_scope' || /out.?of.?scope|owner_files/i.test(reason)) {
|
|
3503
|
-
return makePlan(
|
|
3504
|
-
'diagnosis_revision',
|
|
3505
|
-
'Revise Diagnosis Scope',
|
|
3506
|
-
'Revise owner_files only when new evidence proves the current scope is wrong.',
|
|
3507
|
-
'diagnosis_revision_only',
|
|
3508
|
-
false,
|
|
3509
|
-
true,
|
|
3510
|
-
[
|
|
3511
|
-
'Compare changed files against the diagnosis owner_files.',
|
|
3512
|
-
'Collect evidence proving why the new file is in the failing path.',
|
|
3513
|
-
'Update accepted_hypothesis, failing_path, owner_files, and proof_plan together.',
|
|
3514
|
-
'Resume repair only after the revised gate validates.'
|
|
3515
|
-
],
|
|
3516
|
-
['out-of-scope file list', 'new failing-path evidence', 'revalidated diagnosis gate'],
|
|
3517
|
-
['revised owner_files with evidence', 'new business proof requirement'],
|
|
3518
|
-
['keep broad edits without diagnosis', 'add convenience refactors', 'continue repair outside owner_files']
|
|
3519
|
-
);
|
|
3520
|
-
}
|
|
3521
|
-
if (action === 'retry_infra') {
|
|
3522
|
-
const isCompile = failureClass === 'compile';
|
|
3523
|
-
return makePlan(
|
|
3524
|
-
isCompile ? 'compile_repair' : 'infra_repair',
|
|
3525
|
-
isCompile ? 'Repair Compile Gate' : 'Repair Infra Gate',
|
|
3526
|
-
isCompile
|
|
3527
|
-
? 'Fix the deterministic compile/build blocker before spending another product repair loop.'
|
|
3528
|
-
: 'Fix the deterministic harness/browser/server/Mongo blocker before product repair.',
|
|
3529
|
-
isCompile ? 'compile_repair_only' : 'infra_repair_only',
|
|
3530
|
-
false,
|
|
3531
|
-
false,
|
|
3532
|
-
isCompile
|
|
3533
|
-
? [
|
|
3534
|
-
'Read the exact compile/build log and identify the first actionable error.',
|
|
3535
|
-
'Fix missing dependency, stale artifact, command, cache, or type wiring required by the compile gate.',
|
|
3536
|
-
'Rerun the same finite compile command.',
|
|
3537
|
-
'Return the compile log artifact and status.'
|
|
3538
|
-
]
|
|
3539
|
-
: [
|
|
3540
|
-
'Run preflight for Puppeteer, Chrome executable, ports, Mongo/settings, and startup command.',
|
|
3541
|
-
'Repair only the failing harness/environment item.',
|
|
3542
|
-
'Rerun the same infra check.',
|
|
3543
|
-
'Return the infra log and pass/fail status.'
|
|
3544
|
-
],
|
|
3545
|
-
isCompile
|
|
3546
|
-
? ['compile command', 'compile log artifact', 'first failing error fixed or still failing']
|
|
3547
|
-
: ['preflight command', 'infra log artifact', 'specific repaired environment item'],
|
|
3548
|
-
['same check now passes', 'new infra/compile blocker hash'],
|
|
3549
|
-
['run product-code model repair', 'count this as product failure', 'accept route/business success without the compile/infra gate']
|
|
3550
|
-
);
|
|
3551
|
-
}
|
|
3552
|
-
if (action === 'park_repeated_failure' || action === 'park_ping_pong') {
|
|
3553
|
-
return makePlan(
|
|
3554
|
-
'blocked_until_new_evidence',
|
|
3555
|
-
action === 'park_ping_pong' ? 'Park Ping-Pong Loop' : 'Park Repeated Failure',
|
|
3556
|
-
'Stop the current loop until new evidence changes the diagnosis, route, or repair target.',
|
|
3557
|
-
'collect_new_evidence_only',
|
|
3558
|
-
false,
|
|
3559
|
-
false,
|
|
3560
|
-
[
|
|
3561
|
-
'Do not rerun the same prompt or same repair.',
|
|
3562
|
-
'Show the repeated blocker and evidence hash.',
|
|
3563
|
-
'Collect a new artifact: failing DOM state, stack trace, network response, Mongo delta, compile log, or revised diagnosis.',
|
|
3564
|
-
'Reset the loop only after material evidence changes the blocker, proof, diagnosis, journey, or actionable trace.'
|
|
3565
|
-
],
|
|
3566
|
-
['same failure count', 'blocker fingerprint', 'evidence hash'],
|
|
3567
|
-
['changed blocker fingerprint', 'changed evidence hash', 'new artifact path', 'business/compile/infra/release proof artifact', 'revised diagnosis or journey contract', 'new actionable trace or data delta'],
|
|
3568
|
-
['alternate between two failed patches', 'increase loop budget without evidence', 'hide the park reason']
|
|
3569
|
-
);
|
|
3570
|
-
}
|
|
3571
|
-
if (failureClass === 'journey') {
|
|
3572
|
-
return makePlan(
|
|
3573
|
-
'journey_contract_repair',
|
|
3574
|
-
'Repair Journey Contract',
|
|
3575
|
-
'Fix the first/next/last workflow contract before app code work continues.',
|
|
3576
|
-
'journey_contract_repair',
|
|
3577
|
-
false,
|
|
3578
|
-
true,
|
|
3579
|
-
[
|
|
3580
|
-
'Open docs/APP_JOURNEY_CONTRACT.md.',
|
|
3581
|
-
'Define first_screen, north_star_workflow, screen_sequence, data_story, completion_states, and qa_assertions.',
|
|
3582
|
-
'Ensure each CTA maps to an action, route, method, calculation, or state transition.',
|
|
3583
|
-
'Validate that sample data can drive the promised workflow.'
|
|
3584
|
-
],
|
|
3585
|
-
['validated journey_contract JSON', 'CTA-to-action mapping', 'workflow QA assertions'],
|
|
3586
|
-
['journey validation passes', 'new workflow QA rows generated'],
|
|
3587
|
-
['build empty routes', 'add link-only dashboard actions', 'defer workflow design to wow pass']
|
|
3588
|
-
);
|
|
3589
|
-
}
|
|
3590
|
-
if (failureClass === 'release') {
|
|
3591
|
-
return makePlan(
|
|
3592
|
-
'release_repair',
|
|
3593
|
-
'Repair Release Gate',
|
|
3594
|
-
'Repair deploy/publish/sample-data release evidence with a hotfix-first path before any repeated full deploy.',
|
|
3595
|
-
'release_repair_only',
|
|
3596
|
-
false,
|
|
3597
|
-
false,
|
|
3598
|
-
[
|
|
3599
|
-
'Read the deploy/publish/sample-data log.',
|
|
3600
|
-
'Identify whether the blocker is domain, asset, seed data, route, permission, or CDN.',
|
|
3601
|
-
'Prefer backend hotfix, release config, domain, cache, or seed-data repair when the product artifact already has business proof.',
|
|
3602
|
-
'Rerun only the failed release gate.'
|
|
3603
|
-
],
|
|
3604
|
-
['deploy or publish log', 'sample-data status', 'failed release gate rerun'],
|
|
3605
|
-
['release gate passes', 'new release blocker hash'],
|
|
3606
|
-
['change core workflow after business QA passed', 'mark accepted from scorecard only', 'rerun full builder loop', 'repeat deploy of same artifact without force_deploy evidence']
|
|
3607
|
-
);
|
|
3608
|
-
}
|
|
3609
|
-
if (failureClass === 'business' || failureClass === 'qa_evidence' || failureClass === 'route') {
|
|
3610
|
-
return makePlan(
|
|
3611
|
-
'business_proof_repair',
|
|
3612
|
-
'Repair Business Proof',
|
|
3613
|
-
'Fix the exact failing workflow assertion and prove before/action/after behavior.',
|
|
3614
|
-
'business_repair',
|
|
3615
|
-
true,
|
|
3616
|
-
true,
|
|
3617
|
-
[
|
|
3618
|
-
'Open the failing QA row or proof_plan.',
|
|
3619
|
-
'Trace the UI action to method/publication/query/calculation.',
|
|
3620
|
-
'Patch the smallest owner or workflow files.',
|
|
3621
|
-
'Rerun only the failed business assertion.',
|
|
3622
|
-
'Record DOM/data/Mongo/artifact proof.'
|
|
3623
|
-
],
|
|
3624
|
-
['failing assertion', 'action trace', 'business proof artifact'],
|
|
3625
|
-
['business assertion passes', 'new failed assertion with new evidence'],
|
|
3626
|
-
['treat route load as success', 'rerun unrelated QA rows first', 'accept scorecard-only evidence']
|
|
3627
|
-
);
|
|
3628
|
-
}
|
|
3629
|
-
if (input.productRepairFailure !== false || failureClass === 'product_code') {
|
|
3630
|
-
return makePlan(
|
|
3631
|
-
'product_code_repair',
|
|
3632
|
-
'Run Targeted Product Repair',
|
|
3633
|
-
'Repair the current product-code blocker with the smallest scoped edit and immediate proof.',
|
|
3634
|
-
'targeted_product_repair',
|
|
3635
|
-
true,
|
|
3636
|
-
true,
|
|
3637
|
-
[
|
|
3638
|
-
'Inspect the current blocker, artifacts, changed files, and owner/workflow scope.',
|
|
3639
|
-
'Form one small repair hypothesis.',
|
|
3640
|
-
'Patch only files justified by the active diagnosis or journey contract.',
|
|
3641
|
-
'Rerun the smallest failed gate.',
|
|
3642
|
-
'Record new evidence before any additional loop.'
|
|
3643
|
-
],
|
|
3644
|
-
['owner/workflow scope', 'repair diff', 'failed gate rerun artifact'],
|
|
3645
|
-
['new evidence hash', 'same gate passes'],
|
|
3646
|
-
['broad repo search-and-edit', 'rerun the same prompt without inspecting artifacts', 'change unrelated UI polish']
|
|
3647
|
-
);
|
|
3648
|
-
}
|
|
3649
|
-
return makePlan(
|
|
3650
|
-
'continue',
|
|
3651
|
-
'Continue Current Gate',
|
|
3652
|
-
'Continue the current lane because no blocking loop or infra condition was detected.',
|
|
3653
|
-
'continue',
|
|
3654
|
-
false,
|
|
3655
|
-
false,
|
|
3656
|
-
[
|
|
3657
|
-
'Use the active lane memory.',
|
|
3658
|
-
'Run the next finite gate.',
|
|
3659
|
-
'Record artifacts and evidence hash.'
|
|
3660
|
-
],
|
|
3661
|
-
['active lane memory', 'finite gate artifact'],
|
|
3662
|
-
['new gate result', 'new artifact path'],
|
|
3663
|
-
['spawn duplicate runners', 'advance without evidence']
|
|
3664
|
-
);
|
|
3665
|
-
}
|
|
3666
|
-
|
|
3667
|
-
function collectOpenTail(records: ResolveIOAIManagerFailureRecord[]): ResolveIOAIManagerFailureRecord[] {
|
|
3668
|
-
const tail: ResolveIOAIManagerFailureRecord[] = [];
|
|
3669
|
-
for (let index = records.length - 1; index >= 0; index -= 1) {
|
|
3670
|
-
const record = records[index];
|
|
3671
|
-
if (isPassingOutcome(record)) {
|
|
3672
|
-
break;
|
|
3673
|
-
}
|
|
3674
|
-
tail.unshift(record);
|
|
3675
|
-
}
|
|
3676
|
-
return tail;
|
|
3677
|
-
}
|
|
3678
|
-
|
|
3679
|
-
function countSameFailure(records: ResolveIOAIManagerFailureRecord[], current: ResolveIOAIManagerFailureRecord): number {
|
|
3680
|
-
const currentKey = failureKey(current);
|
|
3681
|
-
let count = 0;
|
|
3682
|
-
for (let index = records.length - 1; index >= 0; index -= 1) {
|
|
3683
|
-
const record = records[index];
|
|
3684
|
-
if (isPassingOutcome(record)) {
|
|
3685
|
-
break;
|
|
3686
|
-
}
|
|
3687
|
-
if (failureKey(record) !== currentKey) {
|
|
3688
|
-
break;
|
|
3689
|
-
}
|
|
3690
|
-
count += 1;
|
|
3691
|
-
}
|
|
3692
|
-
return count;
|
|
3693
|
-
}
|
|
3694
|
-
|
|
3695
|
-
function previousRecordForPolicy(
|
|
3696
|
-
history: ResolveIOAIManagerFailureRecord[],
|
|
3697
|
-
current: ResolveIOAIManagerFailureRecord,
|
|
3698
|
-
currentWasExplicit: boolean
|
|
3699
|
-
): ResolveIOAIManagerFailureRecord | undefined {
|
|
3700
|
-
if (!history.length) {
|
|
3701
|
-
return undefined;
|
|
3702
|
-
}
|
|
3703
|
-
if (!currentWasExplicit) {
|
|
3704
|
-
return history.length > 1 ? history[history.length - 2] : undefined;
|
|
3705
|
-
}
|
|
3706
|
-
const last = history[history.length - 1];
|
|
3707
|
-
if (failureEvidenceKey(last) === failureEvidenceKey(current)) {
|
|
3708
|
-
return history.length > 1 ? history[history.length - 2] : undefined;
|
|
3709
|
-
}
|
|
3710
|
-
return last;
|
|
3711
|
-
}
|
|
3712
|
-
|
|
3713
|
-
function countPingPong(records: ResolveIOAIManagerFailureRecord[], current: ResolveIOAIManagerFailureRecord): number {
|
|
3714
|
-
const tail = collectOpenTail(records).filter((record) => {
|
|
3715
|
-
return cleanText(record.lane, 80) === cleanText(current.lane, 80)
|
|
3716
|
-
&& cleanText(record.stepType, 80) === cleanText(current.stepType, 80);
|
|
3717
|
-
});
|
|
3718
|
-
if (tail.length < 4) {
|
|
3719
|
-
return 0;
|
|
3720
|
-
}
|
|
3721
|
-
const keys = tail.map(failureKey).filter(Boolean);
|
|
3722
|
-
const last = keys.slice(-6);
|
|
3723
|
-
if (last.length < 4) {
|
|
3724
|
-
return 0;
|
|
3725
|
-
}
|
|
3726
|
-
const distinct = Array.from(new Set(last));
|
|
3727
|
-
if (distinct.length !== 2) {
|
|
3728
|
-
return 0;
|
|
3729
|
-
}
|
|
3730
|
-
for (let index = 2; index < last.length; index += 1) {
|
|
3731
|
-
if (last[index] !== last[index - 2]) {
|
|
3732
|
-
return 0;
|
|
3733
|
-
}
|
|
3734
|
-
}
|
|
3735
|
-
return last.length - 1;
|
|
3736
|
-
}
|
|
3737
|
-
|
|
3738
|
-
export function decideResolveIOAIManagerPolicy(input: ResolveIOAIManagerPolicyInput): ResolveIOAIManagerPolicyDecision {
|
|
3739
|
-
const history = Array.isArray(input.history) ? input.history : [];
|
|
3740
|
-
const currentWasExplicit = !!input.current;
|
|
3741
|
-
const current = input.current || history[history.length - 1] || {};
|
|
3742
|
-
const failureClass = normalizeResolveIOAIManagerFailureClass(current.failureClass);
|
|
3743
|
-
const blockerFingerprint = resolveResolveIOAIManagerBlockerFingerprint(current);
|
|
3744
|
-
const evidenceHash = hashResolveIOAIManagerEvidence(current);
|
|
3745
|
-
const previous = previousRecordForPolicy(history, current, currentWasExplicit);
|
|
3746
|
-
const previousSameFailure = previous && failureKey(previous) === failureKey(current);
|
|
3747
|
-
const evidenceAssessment = previousSameFailure
|
|
3748
|
-
? assessResolveIOAIManagerEvidenceChange(previous, current)
|
|
3749
|
-
: {
|
|
3750
|
-
changed: false,
|
|
3751
|
-
material: false,
|
|
3752
|
-
strength: 'none' as ResolveIOAIManagerEvidenceStrength,
|
|
3753
|
-
signals: [],
|
|
3754
|
-
evidenceHash,
|
|
3755
|
-
blockerFingerprint
|
|
3756
|
-
};
|
|
3757
|
-
const newEvidence = !!previousSameFailure && evidenceAssessment.changed;
|
|
3758
|
-
const sameFailureCount = countSameFailure(history, current);
|
|
3759
|
-
const pingPongCount = countPingPong(history, current);
|
|
3760
|
-
const maxSameFailureRepeats = Math.max(1, Number(input.maxSameFailureRepeats || 3) || 3);
|
|
3761
|
-
const maxPingPongTransitions = Math.max(3, Number(input.maxPingPongTransitions || 3) || 3);
|
|
3762
|
-
const infraClasses = new Set((input.infraFailureClasses || ['infra', 'compile']).map(normalizeResolveIOAIManagerFailureClass));
|
|
3763
|
-
const ignoredClasses = new Set((input.ignoredFailureClasses || []).map(normalizeResolveIOAIManagerFailureClass));
|
|
3764
|
-
const base = {
|
|
3765
|
-
failureClass,
|
|
3766
|
-
blockerFingerprint,
|
|
3767
|
-
evidenceHash,
|
|
3768
|
-
sameFailureCount,
|
|
3769
|
-
pingPongCount,
|
|
3770
|
-
newEvidence,
|
|
3771
|
-
materialEvidence: evidenceAssessment.material,
|
|
3772
|
-
evidenceStrength: evidenceAssessment.strength,
|
|
3773
|
-
evidenceSignals: evidenceAssessment.signals,
|
|
3774
|
-
loopBudgetShouldReset: false,
|
|
3775
|
-
productRepairFailure: !infraClasses.has(failureClass) && !ignoredClasses.has(failureClass)
|
|
3776
|
-
};
|
|
3777
|
-
const withPlan = (
|
|
3778
|
-
action: ResolveIOAIManagerAction,
|
|
3779
|
-
reason: string,
|
|
3780
|
-
overrides: Partial<Omit<ResolveIOAIManagerPolicyDecision, 'action' | 'reason' | 'recoveryPlan' | 'recoveryCheckpoint'>> = {}
|
|
3781
|
-
): ResolveIOAIManagerPolicyDecision => {
|
|
3782
|
-
const merged = {
|
|
3783
|
-
...base,
|
|
3784
|
-
...overrides
|
|
3785
|
-
};
|
|
3786
|
-
const recoveryPlan = buildResolveIOAIManagerRecoveryPlan({
|
|
3787
|
-
action,
|
|
3788
|
-
reason,
|
|
3789
|
-
failureClass: merged.failureClass,
|
|
3790
|
-
lane: current.lane,
|
|
3791
|
-
stepType: current.stepType,
|
|
3792
|
-
blocker: current.blocker || current.summary,
|
|
3793
|
-
sameFailureCount: merged.sameFailureCount,
|
|
3794
|
-
pingPongCount: merged.pingPongCount,
|
|
3795
|
-
newEvidence: merged.newEvidence,
|
|
3796
|
-
productRepairFailure: merged.productRepairFailure,
|
|
3797
|
-
changedFiles: current.changedFiles,
|
|
3798
|
-
artifactPaths: current.artifactPaths,
|
|
3799
|
-
maxSameFailureRepeats
|
|
3800
|
-
});
|
|
3801
|
-
const recoveryCheckpoint = buildResolveIOAIManagerRecoveryCheckpoint({
|
|
3802
|
-
plan: recoveryPlan,
|
|
3803
|
-
current
|
|
3804
|
-
});
|
|
3805
|
-
const recoveryEvidenceProbe = buildResolveIOAIManagerRecoveryEvidenceProbe({
|
|
3806
|
-
checkpoint: recoveryCheckpoint,
|
|
3807
|
-
current
|
|
3808
|
-
});
|
|
3809
|
-
return {
|
|
3810
|
-
...merged,
|
|
3811
|
-
action,
|
|
3812
|
-
reason,
|
|
3813
|
-
recoveryPlan,
|
|
3814
|
-
recoveryCheckpoint,
|
|
3815
|
-
recoveryEvidenceProbe,
|
|
3816
|
-
recoveryAction: buildResolveIOAIManagerRecoveryActionPacket({
|
|
3817
|
-
plan: recoveryPlan,
|
|
3818
|
-
checkpoint: recoveryCheckpoint,
|
|
3819
|
-
probe: recoveryEvidenceProbe,
|
|
3820
|
-
current
|
|
3821
|
-
})
|
|
3822
|
-
};
|
|
3823
|
-
};
|
|
3824
|
-
if (isPassingOutcome(current)) {
|
|
3825
|
-
return withPlan('reset_loop_budget', 'manager_policy_progress_passed', {
|
|
3826
|
-
loopBudgetShouldReset: true,
|
|
3827
|
-
productRepairFailure: false
|
|
3828
|
-
});
|
|
3829
|
-
}
|
|
3830
|
-
if (isManualOutcome(current)) {
|
|
3831
|
-
return withPlan('manual_handoff', 'manager_policy_manual_or_stopped');
|
|
3832
|
-
}
|
|
3833
|
-
if (pingPongCount >= maxPingPongTransitions) {
|
|
3834
|
-
return withPlan('park_ping_pong', 'manager_policy_ping_pong_failure_loop');
|
|
3835
|
-
}
|
|
3836
|
-
if (sameFailureCount >= maxSameFailureRepeats && !evidenceAssessment.material) {
|
|
3837
|
-
return withPlan('park_repeated_failure', newEvidence
|
|
3838
|
-
? 'manager_policy_same_failure_with_weak_evidence'
|
|
3839
|
-
: 'manager_policy_same_failure_without_new_evidence');
|
|
3840
|
-
}
|
|
3841
|
-
if (infraClasses.has(failureClass)) {
|
|
3842
|
-
return withPlan('retry_infra', 'manager_policy_infra_failure_routes_to_infra_repair', {
|
|
3843
|
-
productRepairFailure: false
|
|
3844
|
-
});
|
|
3845
|
-
}
|
|
3846
|
-
if (newEvidence || !previousSameFailure) {
|
|
3847
|
-
if (newEvidence && !evidenceAssessment.material) {
|
|
3848
|
-
return withPlan('continue', 'manager_policy_weak_evidence_does_not_reset_loop_budget');
|
|
3849
|
-
}
|
|
3850
|
-
return withPlan('continue', newEvidence
|
|
3851
|
-
? 'manager_policy_new_material_evidence_resets_loop_budget'
|
|
3852
|
-
: 'manager_policy_new_failure_or_lane', {
|
|
3853
|
-
loopBudgetShouldReset: true,
|
|
3854
|
-
materialEvidence: newEvidence ? true : base.materialEvidence,
|
|
3855
|
-
evidenceStrength: newEvidence ? evidenceAssessment.strength : base.evidenceStrength
|
|
3856
|
-
});
|
|
3857
|
-
}
|
|
3858
|
-
return withPlan('continue', 'manager_policy_retry_below_repeat_limit');
|
|
3859
|
-
}
|
|
3860
|
-
|
|
3861
|
-
function buildReplayDirectiveFromPlan(input: {
|
|
3862
|
-
surface: string;
|
|
3863
|
-
plan: ResolveIOAIManagerRecoveryPlan;
|
|
3864
|
-
current: ResolveIOAIManagerFailureRecord;
|
|
3865
|
-
now?: Date | string;
|
|
3866
|
-
}): ResolveIOAIManagerRecoveryExecutionDirective {
|
|
3867
|
-
const checkpoint = buildResolveIOAIManagerRecoveryCheckpoint({
|
|
3868
|
-
plan: input.plan,
|
|
3869
|
-
current: input.current,
|
|
3870
|
-
now: input.now
|
|
3871
|
-
});
|
|
3872
|
-
const probe = buildResolveIOAIManagerRecoveryEvidenceProbe({
|
|
3873
|
-
checkpoint,
|
|
3874
|
-
current: input.current,
|
|
3875
|
-
now: input.now
|
|
3876
|
-
});
|
|
3877
|
-
const action = buildResolveIOAIManagerRecoveryActionPacket({
|
|
3878
|
-
plan: input.plan,
|
|
3879
|
-
checkpoint,
|
|
3880
|
-
probe,
|
|
3881
|
-
current: input.current,
|
|
3882
|
-
now: input.now
|
|
3883
|
-
});
|
|
3884
|
-
const dispatch = decideResolveIOAIManagerRecoveryActionDispatch({
|
|
3885
|
-
action,
|
|
3886
|
-
current: input.current,
|
|
3887
|
-
now: input.now
|
|
3888
|
-
});
|
|
3889
|
-
return buildResolveIOAIManagerRecoveryExecutionDirective({
|
|
3890
|
-
action,
|
|
3891
|
-
dispatchDecision: dispatch,
|
|
3892
|
-
current: input.current,
|
|
3893
|
-
surface: input.surface,
|
|
3894
|
-
now: input.now
|
|
3895
|
-
});
|
|
3896
|
-
}
|
|
3897
|
-
|
|
3898
|
-
function replayCaseFromDirective(input: {
|
|
3899
|
-
caseId: string;
|
|
3900
|
-
surface: string;
|
|
3901
|
-
action: string;
|
|
3902
|
-
reason: string;
|
|
3903
|
-
directive: ResolveIOAIManagerRecoveryExecutionDirective;
|
|
3904
|
-
expectedSafeAutoDispatch: boolean;
|
|
3905
|
-
pass: boolean;
|
|
3906
|
-
loopBudgetShouldReset?: boolean;
|
|
3907
|
-
materialEvidence?: boolean;
|
|
3908
|
-
evidenceStrength?: ResolveIOAIManagerEvidenceStrength;
|
|
3909
|
-
details?: Record<string, any>;
|
|
3910
|
-
}): ResolveIOAIManagerRecoveryReplayCase {
|
|
3911
|
-
const directive = input.directive;
|
|
3912
|
-
const safeAutoDispatch = isResolveIOAIManagerSafeAutoDispatch({
|
|
3913
|
-
dispatchAction: directive.dispatchAction,
|
|
3914
|
-
directive,
|
|
3915
|
-
includeJourneyContractRepair: input.expectedSafeAutoDispatch && directive.dispatchAction === 'run_journey_contract_repair',
|
|
3916
|
-
includeReleaseRepair: input.expectedSafeAutoDispatch && directive.dispatchAction === 'run_release_repair',
|
|
3917
|
-
includeProductRepair: false
|
|
3918
|
-
});
|
|
3919
|
-
return {
|
|
3920
|
-
caseId: input.caseId,
|
|
3921
|
-
surface: input.surface,
|
|
3922
|
-
pass: input.pass && safeAutoDispatch === input.expectedSafeAutoDispatch,
|
|
3923
|
-
action: input.action,
|
|
3924
|
-
reason: input.reason,
|
|
3925
|
-
recoveryClass: cleanText(directive.dispatchRecord?.mode || directive.phase, 120),
|
|
3926
|
-
dispatchAction: directive.dispatchAction,
|
|
3927
|
-
safeAutoDispatch,
|
|
3928
|
-
expectedSafeAutoDispatch: input.expectedSafeAutoDispatch,
|
|
3929
|
-
productRepairAllowed: directive.canRunProductRepair === true,
|
|
3930
|
-
loopBudgetShouldReset: input.loopBudgetShouldReset === true,
|
|
3931
|
-
materialEvidence: input.materialEvidence === true,
|
|
3932
|
-
evidenceStrength: input.evidenceStrength || 'none',
|
|
3933
|
-
...(directive.releasePolicy ? {
|
|
3934
|
-
hotfixFirst: directive.releasePolicy.policy === 'hotfix_first',
|
|
3935
|
-
fullDeployAllowed: directive.releasePolicy.fullDeployAllowed
|
|
3936
|
-
} : {}),
|
|
3937
|
-
details: {
|
|
3938
|
-
phase: directive.phase,
|
|
3939
|
-
allowed: directive.allowed,
|
|
3940
|
-
reason: directive.reason,
|
|
3941
|
-
releasePolicy: directive.releasePolicy,
|
|
3942
|
-
...(input.details || {})
|
|
3943
|
-
}
|
|
3944
|
-
};
|
|
3945
|
-
}
|
|
3946
|
-
|
|
3947
|
-
export function buildResolveIOAIManagerRecoveryReplayMatrix(
|
|
3948
|
-
input: ResolveIOAIManagerRecoveryReplayMatrixInput = {}
|
|
3949
|
-
): ResolveIOAIManagerRecoveryReplayCase[] {
|
|
3950
|
-
const surface = cleanText(input.surface || 'runner', 120);
|
|
3951
|
-
const maxSameFailureRepeats = Math.max(1, Number(input.maxSameFailureRepeats || 2) || 2);
|
|
3952
|
-
const includeReleaseRepair = input.includeReleaseRepair !== false;
|
|
3953
|
-
const includeJourneyContractRepair = input.includeJourneyContractRepair !== false;
|
|
3954
|
-
const cases: ResolveIOAIManagerRecoveryReplayCase[] = [];
|
|
3955
|
-
const infraPolicy = decideResolveIOAIManagerPolicy({
|
|
3956
|
-
history: [
|
|
3957
|
-
{ lane: 'qa', stepType: 'workflow_qa', outcome: 'needs_repair', failureClass: 'infra', blocker: 'Chrome executable missing.', evidenceHash: 'chrome-missing' }
|
|
3958
|
-
],
|
|
3959
|
-
current: { lane: 'qa', stepType: 'workflow_qa', outcome: 'needs_repair', failureClass: 'infra', blocker: 'Chrome executable missing.', evidenceHash: 'chrome-missing' },
|
|
3960
|
-
maxSameFailureRepeats
|
|
3961
|
-
});
|
|
3962
|
-
const infraDirective = buildResolveIOAIManagerRecoveryExecutionDirective({
|
|
3963
|
-
action: infraPolicy.recoveryAction,
|
|
3964
|
-
current: { lane: 'qa', stepType: 'workflow_qa', outcome: 'needs_repair', failureClass: 'infra', blocker: 'Chrome executable missing.', evidenceHash: 'chrome-missing' },
|
|
3965
|
-
surface
|
|
3966
|
-
});
|
|
3967
|
-
cases.push(replayCaseFromDirective({
|
|
3968
|
-
caseId: 'infra_routes_to_infra_repair',
|
|
3969
|
-
surface,
|
|
3970
|
-
action: infraPolicy.action,
|
|
3971
|
-
reason: infraPolicy.reason,
|
|
3972
|
-
directive: infraDirective,
|
|
3973
|
-
expectedSafeAutoDispatch: true,
|
|
3974
|
-
pass: infraPolicy.action === 'retry_infra'
|
|
3975
|
-
&& infraPolicy.recoveryPlan.recoveryClass === 'infra_repair'
|
|
3976
|
-
&& infraDirective.dispatchAction === 'run_infra_repair'
|
|
3977
|
-
&& infraDirective.canRunProductRepair === false,
|
|
3978
|
-
details: { recoveryClass: infraPolicy.recoveryPlan.recoveryClass }
|
|
3979
|
-
}));
|
|
3980
|
-
const releasePlan = buildResolveIOAIManagerRecoveryPlan({
|
|
3981
|
-
action: 'continue',
|
|
3982
|
-
reason: 'publish failed after business proof',
|
|
3983
|
-
failureClass: 'release',
|
|
3984
|
-
lane: 'publish',
|
|
3985
|
-
stepType: 'test_deploy',
|
|
3986
|
-
blocker: 'Domain publish failed for the current artifact.'
|
|
3987
|
-
});
|
|
3988
|
-
const releaseCurrent = {
|
|
3989
|
-
lane: 'publish',
|
|
3990
|
-
stepType: 'test_deploy',
|
|
3991
|
-
outcome: 'needs_repair',
|
|
3992
|
-
failureClass: 'release',
|
|
3993
|
-
blocker: 'Domain publish failed for the current artifact.',
|
|
3994
|
-
evidenceHash: 'release-domain-publish-failed',
|
|
3995
|
-
artifactPaths: ['runner-evidence/release-status.json']
|
|
3996
|
-
};
|
|
3997
|
-
const releaseDirective = buildReplayDirectiveFromPlan({
|
|
3998
|
-
surface,
|
|
3999
|
-
plan: releasePlan,
|
|
4000
|
-
current: releaseCurrent
|
|
4001
|
-
});
|
|
4002
|
-
cases.push(replayCaseFromDirective({
|
|
4003
|
-
caseId: 'release_uses_hotfix_first_repair',
|
|
4004
|
-
surface,
|
|
4005
|
-
action: 'continue',
|
|
4006
|
-
reason: 'publish failed after business proof',
|
|
4007
|
-
directive: releaseDirective,
|
|
4008
|
-
expectedSafeAutoDispatch: includeReleaseRepair,
|
|
4009
|
-
pass: releasePlan.recoveryClass === 'release_repair'
|
|
4010
|
-
&& releaseDirective.dispatchAction === 'run_release_repair'
|
|
4011
|
-
&& releaseDirective.releasePolicy?.policy === 'hotfix_first'
|
|
4012
|
-
&& releaseDirective.releasePolicy?.fullDeployAllowed === false
|
|
4013
|
-
&& releaseDirective.canRunProductRepair === false,
|
|
4014
|
-
details: { recoveryClass: releasePlan.recoveryClass }
|
|
4015
|
-
}));
|
|
4016
|
-
const journeyPlan = buildResolveIOAIManagerRecoveryPlan({
|
|
4017
|
-
action: 'continue',
|
|
4018
|
-
reason: 'journey contract missing first next last',
|
|
4019
|
-
failureClass: 'journey',
|
|
4020
|
-
lane: 'plan',
|
|
4021
|
-
stepType: 'journey_contract',
|
|
4022
|
-
blocker: 'Journey contract does not map the hub CTA to an action.'
|
|
4023
|
-
});
|
|
4024
|
-
const journeyDirective = buildReplayDirectiveFromPlan({
|
|
4025
|
-
surface,
|
|
4026
|
-
plan: journeyPlan,
|
|
4027
|
-
current: {
|
|
4028
|
-
lane: 'plan',
|
|
4029
|
-
stepType: 'journey_contract',
|
|
4030
|
-
outcome: 'needs_repair',
|
|
4031
|
-
failureClass: 'journey',
|
|
4032
|
-
blocker: 'Journey contract does not map the hub CTA to an action.',
|
|
4033
|
-
evidenceHash: 'journey-contract-invalid'
|
|
4034
|
-
}
|
|
4035
|
-
});
|
|
4036
|
-
cases.push(replayCaseFromDirective({
|
|
4037
|
-
caseId: 'journey_repair_is_safe_only_for_aicoder_surfaces',
|
|
4038
|
-
surface,
|
|
4039
|
-
action: 'continue',
|
|
4040
|
-
reason: 'journey contract missing first next last',
|
|
4041
|
-
directive: journeyDirective,
|
|
4042
|
-
expectedSafeAutoDispatch: includeJourneyContractRepair,
|
|
4043
|
-
pass: journeyPlan.recoveryClass === 'journey_contract_repair'
|
|
4044
|
-
&& journeyDirective.dispatchAction === 'run_journey_contract_repair'
|
|
4045
|
-
&& journeyDirective.canRunProductRepair === false,
|
|
4046
|
-
details: { recoveryClass: journeyPlan.recoveryClass }
|
|
4047
|
-
}));
|
|
4048
|
-
const weakPolicy = decideResolveIOAIManagerPolicy({
|
|
4049
|
-
history: [
|
|
4050
|
-
{ lane: 'repair', stepType: 'repair', outcome: 'needs_repair', failureClass: 'product_code', blocker: 'Save action still throws TypeError.', evidenceHash: 'save-before' },
|
|
4051
|
-
{ lane: 'repair', stepType: 'repair', outcome: 'needs_repair', failureClass: 'product_code', blocker: 'Save action still throws TypeError.', evidenceHash: 'renamed-hash-only' }
|
|
4052
|
-
],
|
|
4053
|
-
current: { lane: 'repair', stepType: 'repair', outcome: 'needs_repair', failureClass: 'product_code', blocker: 'Save action still throws TypeError.', evidenceHash: 'renamed-hash-only' },
|
|
4054
|
-
maxSameFailureRepeats
|
|
4055
|
-
});
|
|
4056
|
-
const weakDirective = buildResolveIOAIManagerRecoveryExecutionDirective({
|
|
4057
|
-
action: weakPolicy.recoveryAction,
|
|
4058
|
-
current: { lane: 'repair', stepType: 'repair', outcome: 'needs_repair', failureClass: 'product_code', blocker: 'Save action still throws TypeError.', evidenceHash: 'renamed-hash-only' },
|
|
4059
|
-
surface
|
|
4060
|
-
});
|
|
4061
|
-
cases.push(replayCaseFromDirective({
|
|
4062
|
-
caseId: 'weak_hash_only_evidence_stays_parked',
|
|
4063
|
-
surface,
|
|
4064
|
-
action: weakPolicy.action,
|
|
4065
|
-
reason: weakPolicy.reason,
|
|
4066
|
-
directive: weakDirective,
|
|
4067
|
-
expectedSafeAutoDispatch: true,
|
|
4068
|
-
pass: weakPolicy.action === 'park_repeated_failure'
|
|
4069
|
-
&& weakPolicy.materialEvidence === false
|
|
4070
|
-
&& weakPolicy.loopBudgetShouldReset === false
|
|
4071
|
-
&& weakDirective.dispatchAction === 'run_evidence_probe',
|
|
4072
|
-
loopBudgetShouldReset: weakPolicy.loopBudgetShouldReset,
|
|
4073
|
-
materialEvidence: weakPolicy.materialEvidence,
|
|
4074
|
-
evidenceStrength: weakPolicy.evidenceStrength,
|
|
4075
|
-
details: { evidenceSignals: weakPolicy.evidenceSignals }
|
|
4076
|
-
}));
|
|
4077
|
-
const materialPolicy = decideResolveIOAIManagerPolicy({
|
|
4078
|
-
history: [
|
|
4079
|
-
{ lane: 'repair', stepType: 'repair', outcome: 'needs_repair', failureClass: 'product_code', blocker: 'Save action still throws TypeError.', evidenceHash: 'save-before', artifactPaths: ['qa/save-before.log'] },
|
|
4080
|
-
{
|
|
4081
|
-
lane: 'repair',
|
|
4082
|
-
stepType: 'repair',
|
|
4083
|
-
outcome: 'needs_repair',
|
|
4084
|
-
failureClass: 'product_code',
|
|
4085
|
-
blocker: 'Save action still throws TypeError.',
|
|
4086
|
-
summary: 'New DOM trace proves the click reaches the method with an undefined id.',
|
|
4087
|
-
evidenceHash: 'save-dom-trace',
|
|
4088
|
-
artifactPaths: ['qa/save-before.log', 'qa/save-dom-trace.json']
|
|
4089
|
-
}
|
|
4090
|
-
],
|
|
4091
|
-
current: {
|
|
4092
|
-
lane: 'repair',
|
|
4093
|
-
stepType: 'repair',
|
|
4094
|
-
outcome: 'needs_repair',
|
|
4095
|
-
failureClass: 'product_code',
|
|
4096
|
-
blocker: 'Save action still throws TypeError.',
|
|
4097
|
-
summary: 'New DOM trace proves the click reaches the method with an undefined id.',
|
|
4098
|
-
evidenceHash: 'save-dom-trace',
|
|
4099
|
-
artifactPaths: ['qa/save-before.log', 'qa/save-dom-trace.json']
|
|
4100
|
-
},
|
|
4101
|
-
maxSameFailureRepeats
|
|
4102
|
-
});
|
|
4103
|
-
cases.push({
|
|
4104
|
-
caseId: 'material_evidence_resets_loop_budget',
|
|
4105
|
-
surface,
|
|
4106
|
-
pass: materialPolicy.action === 'continue'
|
|
4107
|
-
&& materialPolicy.materialEvidence === true
|
|
4108
|
-
&& materialPolicy.loopBudgetShouldReset === true,
|
|
4109
|
-
action: materialPolicy.action,
|
|
4110
|
-
reason: materialPolicy.reason,
|
|
4111
|
-
recoveryClass: materialPolicy.recoveryPlan.recoveryClass,
|
|
4112
|
-
dispatchAction: materialPolicy.recoveryAction.mode,
|
|
4113
|
-
safeAutoDispatch: false,
|
|
4114
|
-
expectedSafeAutoDispatch: false,
|
|
4115
|
-
productRepairAllowed: materialPolicy.recoveryAction.productRepairAllowed,
|
|
4116
|
-
loopBudgetShouldReset: materialPolicy.loopBudgetShouldReset,
|
|
4117
|
-
materialEvidence: materialPolicy.materialEvidence,
|
|
4118
|
-
evidenceStrength: materialPolicy.evidenceStrength,
|
|
4119
|
-
details: { evidenceSignals: materialPolicy.evidenceSignals }
|
|
4120
|
-
});
|
|
4121
|
-
const productPlan = buildResolveIOAIManagerRecoveryPlan({
|
|
4122
|
-
action: 'continue',
|
|
4123
|
-
reason: 'business proof still failing',
|
|
4124
|
-
failureClass: 'product_code',
|
|
4125
|
-
lane: 'repair',
|
|
4126
|
-
stepType: 'repair',
|
|
4127
|
-
blocker: 'Save action still throws TypeError.',
|
|
4128
|
-
productRepairFailure: true
|
|
4129
|
-
});
|
|
4130
|
-
const productDirective = buildReplayDirectiveFromPlan({
|
|
4131
|
-
surface,
|
|
4132
|
-
plan: productPlan,
|
|
4133
|
-
current: {
|
|
4134
|
-
lane: 'repair',
|
|
4135
|
-
stepType: 'repair',
|
|
4136
|
-
outcome: 'needs_repair',
|
|
4137
|
-
failureClass: 'product_code',
|
|
4138
|
-
blocker: 'Save action still throws TypeError.',
|
|
4139
|
-
evidenceHash: 'save-product-repair'
|
|
4140
|
-
}
|
|
4141
|
-
});
|
|
4142
|
-
const productSafe = isResolveIOAIManagerSafeAutoDispatch({
|
|
4143
|
-
dispatchAction: productDirective.dispatchAction,
|
|
4144
|
-
directive: productDirective,
|
|
4145
|
-
includeProductRepair: input.includeProductRepair === true
|
|
4146
|
-
});
|
|
4147
|
-
cases.push({
|
|
4148
|
-
caseId: 'product_repair_requires_explicit_operator_policy',
|
|
4149
|
-
surface,
|
|
4150
|
-
pass: productPlan.recoveryClass === 'product_code_repair'
|
|
4151
|
-
&& productDirective.dispatchAction === 'run_targeted_product_repair'
|
|
4152
|
-
&& productSafe === false,
|
|
4153
|
-
action: 'continue',
|
|
4154
|
-
reason: 'business proof still failing',
|
|
4155
|
-
recoveryClass: productPlan.recoveryClass,
|
|
4156
|
-
dispatchAction: productDirective.dispatchAction,
|
|
4157
|
-
safeAutoDispatch: productSafe,
|
|
4158
|
-
expectedSafeAutoDispatch: false,
|
|
4159
|
-
productRepairAllowed: productDirective.canRunProductRepair,
|
|
4160
|
-
loopBudgetShouldReset: false,
|
|
4161
|
-
materialEvidence: false,
|
|
4162
|
-
evidenceStrength: 'none',
|
|
4163
|
-
details: { phase: productDirective.phase, allowed: productDirective.allowed }
|
|
4164
|
-
});
|
|
4165
|
-
const duplicateReleasePolicy = buildResolveIOAIManagerHotfixFirstReleasePolicy({
|
|
4166
|
-
surface,
|
|
4167
|
-
deployFingerprint: `${surface}-artifact-sha`,
|
|
4168
|
-
lastDeployFingerprint: `${surface}-artifact-sha`,
|
|
4169
|
-
deployStatus: 'completed',
|
|
4170
|
-
publishStatus: 'published'
|
|
4171
|
-
});
|
|
4172
|
-
cases.push({
|
|
4173
|
-
caseId: 'duplicate_release_fingerprint_blocks_full_deploy',
|
|
4174
|
-
surface,
|
|
4175
|
-
pass: duplicateReleasePolicy.recommendedAction === 'block_duplicate_deploy'
|
|
4176
|
-
&& duplicateReleasePolicy.duplicateDeployBlocked === true
|
|
4177
|
-
&& duplicateReleasePolicy.fullDeployAllowed === false,
|
|
4178
|
-
action: duplicateReleasePolicy.recommendedAction,
|
|
4179
|
-
reason: duplicateReleasePolicy.reason,
|
|
4180
|
-
recoveryClass: 'release_repair',
|
|
4181
|
-
dispatchAction: 'run_release_repair',
|
|
4182
|
-
safeAutoDispatch: includeReleaseRepair,
|
|
4183
|
-
expectedSafeAutoDispatch: includeReleaseRepair,
|
|
4184
|
-
productRepairAllowed: false,
|
|
4185
|
-
loopBudgetShouldReset: false,
|
|
4186
|
-
materialEvidence: false,
|
|
4187
|
-
evidenceStrength: 'none',
|
|
4188
|
-
hotfixFirst: true,
|
|
4189
|
-
fullDeployAllowed: duplicateReleasePolicy.fullDeployAllowed,
|
|
4190
|
-
details: { releasePolicy: duplicateReleasePolicy }
|
|
4191
|
-
});
|
|
4192
|
-
return cases;
|
|
4193
|
-
}
|