@kbediako/codex-orchestrator 0.1.38 → 0.2.1
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/.agents/plugins/marketplace.json +20 -0
- package/README.md +46 -317
- package/bin/codex-orchestrator.js +161 -0
- package/codex.orchestrator.json +149 -13
- package/dist/bin/codex-orchestrator.js +797 -1154
- package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +50 -0
- package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +22 -4
- package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +3 -3
- package/dist/orchestrator/src/cli/adapters/CommandTester.js +2 -2
- package/dist/orchestrator/src/cli/adapters/cloudFailureDiagnostics.js +295 -11
- package/dist/orchestrator/src/cli/coStatusAttachCliShell.js +402 -0
- package/dist/orchestrator/src/cli/coStatusCliShell.js +451 -0
- package/dist/orchestrator/src/cli/coStatusOperatorAutopilotCliShell.js +120 -0
- package/dist/orchestrator/src/cli/codexCliShell.js +119 -0
- package/dist/orchestrator/src/cli/codexDefaultsSetup.js +265 -36
- package/dist/orchestrator/src/cli/config/delegationConfig.js +317 -5
- package/dist/orchestrator/src/cli/config/repoConfigPolicy.js +2 -3
- package/dist/orchestrator/src/cli/config/userConfig.js +28 -13
- package/dist/orchestrator/src/cli/control/authenticatedControlRouteGate.js +69 -0
- package/dist/orchestrator/src/cli/control/authenticatedRouteComposition.js +267 -0
- package/dist/orchestrator/src/cli/control/authenticatedRouteController.js +5 -0
- package/dist/orchestrator/src/cli/control/authenticatedRouteDispatcher.js +41 -0
- package/dist/orchestrator/src/cli/control/compatibilityIssuePresenter.js +1035 -0
- package/dist/orchestrator/src/cli/control/confirmationApproveController.js +62 -0
- package/dist/orchestrator/src/cli/control/confirmationCreateController.js +69 -0
- package/dist/orchestrator/src/cli/control/confirmationIssueConsumeController.js +43 -0
- package/dist/orchestrator/src/cli/control/confirmationListController.js +22 -0
- package/dist/orchestrator/src/cli/control/confirmationValidateController.js +58 -0
- package/dist/orchestrator/src/cli/control/confirmations.js +25 -3
- package/dist/orchestrator/src/cli/control/controlActionCancelConfirmation.js +65 -0
- package/dist/orchestrator/src/cli/control/controlActionController.js +77 -0
- package/dist/orchestrator/src/cli/control/controlActionControllerSequencing.js +161 -0
- package/dist/orchestrator/src/cli/control/controlActionExecution.js +142 -0
- package/dist/orchestrator/src/cli/control/controlActionFinalization.js +43 -0
- package/dist/orchestrator/src/cli/control/controlActionOutcome.js +60 -0
- package/dist/orchestrator/src/cli/control/controlActionPreflight.js +476 -0
- package/dist/orchestrator/src/cli/control/controlAuthenticatedRouteHandoff.js +57 -0
- package/dist/orchestrator/src/cli/control/controlBootstrapAssembly.js +39 -0
- package/dist/orchestrator/src/cli/control/controlBootstrapMetadataPersistence.js +16 -0
- package/dist/orchestrator/src/cli/control/controlEventTransport.js +49 -0
- package/dist/orchestrator/src/cli/control/controlExpiryLifecycle.js +102 -0
- package/dist/orchestrator/src/cli/control/controlHostOwnership.js +480 -0
- package/dist/orchestrator/src/cli/control/controlHostSupervision.js +630 -0
- package/dist/orchestrator/src/cli/control/controlOversightFacade.js +8 -0
- package/dist/orchestrator/src/cli/control/controlOversightReadContract.js +1 -0
- package/dist/orchestrator/src/cli/control/controlOversightReadService.js +16 -0
- package/dist/orchestrator/src/cli/control/controlOversightUpdateContract.js +1 -0
- package/dist/orchestrator/src/cli/control/controlPersistenceFiles.js +6 -0
- package/dist/orchestrator/src/cli/control/controlQuestionChildResolution.js +18 -0
- package/dist/orchestrator/src/cli/control/controlRequestContext.js +42 -0
- package/dist/orchestrator/src/cli/control/controlRequestController.js +9 -0
- package/dist/orchestrator/src/cli/control/controlRequestPredispatch.js +17 -0
- package/dist/orchestrator/src/cli/control/controlRequestRouteDispatch.js +44 -0
- package/dist/orchestrator/src/cli/control/controlRuntime.js +1003 -0
- package/dist/orchestrator/src/cli/control/controlServer.js +23 -1456
- package/dist/orchestrator/src/cli/control/controlServerAuditAndErrorHelpers.js +115 -0
- package/dist/orchestrator/src/cli/control/controlServerAuthenticatedRouteBranch.js +29 -0
- package/dist/orchestrator/src/cli/control/controlServerBootstrapLifecycle.js +30 -0
- package/dist/orchestrator/src/cli/control/controlServerBootstrapStartSequence.js +21 -0
- package/dist/orchestrator/src/cli/control/controlServerOwnedRuntimeLifecycle.js +67 -0
- package/dist/orchestrator/src/cli/control/controlServerPublicLifecycle.js +756 -0
- package/dist/orchestrator/src/cli/control/controlServerPublicRouteHelpers.js +86 -0
- package/dist/orchestrator/src/cli/control/controlServerReadyInstanceLifecycle.js +25 -0
- package/dist/orchestrator/src/cli/control/controlServerReadyInstanceStartup.js +18 -0
- package/dist/orchestrator/src/cli/control/controlServerRequestBodyHelpers.js +37 -0
- package/dist/orchestrator/src/cli/control/controlServerRequestShell.js +40 -0
- package/dist/orchestrator/src/cli/control/controlServerRequestShellBinding.js +17 -0
- package/dist/orchestrator/src/cli/control/controlServerSeedLoading.js +27 -0
- package/dist/orchestrator/src/cli/control/controlServerSeededRuntimeAssembly.js +186 -0
- package/dist/orchestrator/src/cli/control/controlServerStartupInputPreparation.js +31 -0
- package/dist/orchestrator/src/cli/control/controlServerStartupSequence.js +49 -0
- package/dist/orchestrator/src/cli/control/controlState.js +233 -2
- package/dist/orchestrator/src/cli/control/controlStatusDashboard.js +1904 -0
- package/dist/orchestrator/src/cli/control/controlTelegramBridgeBootstrapLifecycle.js +22 -0
- package/dist/orchestrator/src/cli/control/controlTelegramBridgeLifecycle.js +67 -0
- package/dist/orchestrator/src/cli/control/controlTelegramBridgeOversightFacadeFactory.js +8 -0
- package/dist/orchestrator/src/cli/control/controlTelegramCommandController.js +49 -0
- package/dist/orchestrator/src/cli/control/controlTelegramDispatchRead.js +40 -0
- package/dist/orchestrator/src/cli/control/controlTelegramPollingController.js +89 -0
- package/dist/orchestrator/src/cli/control/controlTelegramProjectionNotificationController.js +29 -0
- package/dist/orchestrator/src/cli/control/controlTelegramPushState.js +63 -0
- package/dist/orchestrator/src/cli/control/controlTelegramQuestionRead.js +13 -0
- package/dist/orchestrator/src/cli/control/controlTelegramReadController.js +216 -0
- package/dist/orchestrator/src/cli/control/controlTelegramUpdateHandler.js +63 -0
- package/dist/orchestrator/src/cli/control/controlWatcher.js +73 -5
- package/dist/orchestrator/src/cli/control/delegationRegisterController.js +35 -0
- package/dist/orchestrator/src/cli/control/dynamicToolBridgePolicy.js +139 -0
- package/dist/orchestrator/src/cli/control/eventsSseController.js +12 -0
- package/dist/orchestrator/src/cli/control/linearBudgetState.js +1789 -0
- package/dist/orchestrator/src/cli/control/linearDispatchSource.js +1137 -0
- package/dist/orchestrator/src/cli/control/linearGraphqlClient.js +150 -0
- package/dist/orchestrator/src/cli/control/linearRateLimit.js +102 -0
- package/dist/orchestrator/src/cli/control/linearWebhookController.js +499 -0
- package/dist/orchestrator/src/cli/control/liveLinearAdvisoryRuntime.js +70 -0
- package/dist/orchestrator/src/cli/control/observabilityApiController.js +173 -0
- package/dist/orchestrator/src/cli/control/observabilityReadModel.js +500 -0
- package/dist/orchestrator/src/cli/control/observabilitySurface.js +284 -0
- package/dist/orchestrator/src/cli/control/observabilityUpdateNotifier.js +22 -0
- package/dist/orchestrator/src/cli/control/operatorDashboardPresenter.js +252 -0
- package/dist/orchestrator/src/cli/control/providerAgentCapacity.js +70 -0
- package/dist/orchestrator/src/cli/control/providerControlHostFreshnessGauge.js +1068 -0
- package/dist/orchestrator/src/cli/control/providerIntakeState.js +473 -0
- package/dist/orchestrator/src/cli/control/providerIssueHandoff.js +6811 -0
- package/dist/orchestrator/src/cli/control/providerIssueObservability.js +1348 -0
- package/dist/orchestrator/src/cli/control/providerIssueRetryQueue.js +84 -0
- package/dist/orchestrator/src/cli/control/providerLinearRuntimeProof.js +588 -0
- package/dist/orchestrator/src/cli/control/providerLinearScreenshotProof.js +473 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkerTruth.js +383 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkflowAudit.js +254 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkflowFacade.js +5573 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkflowStates.js +115 -0
- package/dist/orchestrator/src/cli/control/providerMergeCloseout.js +1868 -0
- package/dist/orchestrator/src/cli/control/providerOperatorAutopilot.js +1580 -0
- package/dist/orchestrator/src/cli/control/providerOperatorAutopilotLifecycle.js +154 -0
- package/dist/orchestrator/src/cli/control/providerOperatorAutopilotLocalRolloutExecution.js +1006 -0
- package/dist/orchestrator/src/cli/control/providerPollingHealth.js +435 -0
- package/dist/orchestrator/src/cli/control/providerTerminalCleanup.js +516 -0
- package/dist/orchestrator/src/cli/control/providerWorkerHosts.js +191 -0
- package/dist/orchestrator/src/cli/control/providerWorkflowConfigStore.js +515 -0
- package/dist/orchestrator/src/cli/control/questionChildResolutionAdapter.js +361 -0
- package/dist/orchestrator/src/cli/control/questionQueueController.js +181 -0
- package/dist/orchestrator/src/cli/control/questionReadRetryDeduplication.js +9 -0
- package/dist/orchestrator/src/cli/control/questionReadSequence.js +10 -0
- package/dist/orchestrator/src/cli/control/securityViolationController.js +27 -0
- package/dist/orchestrator/src/cli/control/selectedRunProjection.js +1885 -0
- package/dist/orchestrator/src/cli/control/telegramOversightApiClient.js +48 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridge.js +180 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridgeProjectionDeliveryQueue.js +25 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridgeRuntimeLifecycle.js +45 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridgeStateStore.js +77 -0
- package/dist/orchestrator/src/cli/control/telegramOversightControlActionApiClient.js +45 -0
- package/dist/orchestrator/src/cli/control/trackerDispatchPilot.js +439 -0
- package/dist/orchestrator/src/cli/control/uiDataController.js +34 -0
- package/dist/orchestrator/src/cli/control/uiSessionController.js +100 -0
- package/dist/orchestrator/src/cli/controlHostCliShell.js +860 -0
- package/dist/orchestrator/src/cli/controlHostFreshnessGaugeCliShell.js +129 -0
- package/dist/orchestrator/src/cli/controlHostSupervisionCliShell.js +2127 -0
- package/dist/orchestrator/src/cli/delegationCliShell.js +62 -0
- package/dist/orchestrator/src/cli/delegationServer.js +567 -678
- package/dist/orchestrator/src/cli/delegationServerCliShell.js +52 -0
- package/dist/orchestrator/src/cli/delegationServerQuestionFlowShell.js +228 -0
- package/dist/orchestrator/src/cli/delegationServerToolDispatchShell.js +411 -0
- package/dist/orchestrator/src/cli/delegationServerTransport.js +274 -0
- package/dist/orchestrator/src/cli/delegationSetup.js +51 -171
- package/dist/orchestrator/src/cli/devtoolsCliShell.js +34 -0
- package/dist/orchestrator/src/cli/doctor.js +678 -164
- package/dist/orchestrator/src/cli/doctorCliRequestShell.js +72 -0
- package/dist/orchestrator/src/cli/doctorCliShell.js +138 -0
- package/dist/orchestrator/src/cli/doctorUsage.js +119 -15
- package/dist/orchestrator/src/cli/exec/experience.js +16 -2
- package/dist/orchestrator/src/cli/exec/summary.js +3 -0
- package/dist/orchestrator/src/cli/execCliShell.js +51 -0
- package/dist/orchestrator/src/cli/flowCliRequestShell.js +44 -0
- package/dist/orchestrator/src/cli/flowCliShell.js +239 -0
- package/dist/orchestrator/src/cli/frontendTestCliRequestShell.js +80 -0
- package/dist/orchestrator/src/cli/frontendTestCliShell.js +41 -0
- package/dist/orchestrator/src/cli/init.js +95 -1
- package/dist/orchestrator/src/cli/initCliShell.js +50 -0
- package/dist/orchestrator/src/cli/linearCliShell.js +1200 -0
- package/dist/orchestrator/src/cli/mcpEnableCliShell.js +132 -0
- package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +3 -2
- package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +56 -0
- package/dist/orchestrator/src/cli/orchestrator.js +66 -1376
- package/dist/orchestrator/src/cli/planCliShell.js +19 -0
- package/dist/orchestrator/src/cli/prCliShell.js +41 -0
- package/dist/orchestrator/src/cli/providerLinearChildLanePhaseContract.js +204 -0
- package/dist/orchestrator/src/cli/providerLinearChildLaneRunner.js +1835 -0
- package/dist/orchestrator/src/cli/providerLinearChildLaneShell.js +2420 -0
- package/dist/orchestrator/src/cli/providerLinearChildStreamShell.js +385 -0
- package/dist/orchestrator/src/cli/providerLinearWorkerRunner.js +6834 -0
- package/dist/orchestrator/src/cli/resumeCliShell.js +14 -0
- package/dist/orchestrator/src/cli/reviewCliLaunchShell.js +72 -0
- package/dist/orchestrator/src/cli/rlm/alignment.js +3 -3
- package/dist/orchestrator/src/cli/rlm/context.js +94 -7
- package/dist/orchestrator/src/cli/rlm/rlmCodexRuntimeShell.js +546 -0
- package/dist/orchestrator/src/cli/rlm/symbolic.js +4 -2
- package/dist/orchestrator/src/cli/rlmCliRequestShell.js +42 -0
- package/dist/orchestrator/src/cli/rlmCompletionCliShell.js +46 -0
- package/dist/orchestrator/src/cli/rlmLaunchCliShell.js +51 -0
- package/dist/orchestrator/src/cli/rlmRunner.js +83 -523
- package/dist/orchestrator/src/cli/run/blockMemory.js +500 -0
- package/dist/orchestrator/src/cli/run/manifest.js +410 -73
- package/dist/orchestrator/src/cli/run/manifestPersister.js +45 -14
- package/dist/orchestrator/src/cli/run/runMemoryController.js +216 -0
- package/dist/orchestrator/src/cli/run/source0.js +690 -0
- package/dist/orchestrator/src/cli/run/workspacePath.js +101 -0
- package/dist/orchestrator/src/cli/runtime/mode.js +2 -1
- package/dist/orchestrator/src/cli/runtime/provider.js +39 -2
- package/dist/orchestrator/src/cli/selfCheckCliShell.js +12 -0
- package/dist/orchestrator/src/cli/services/commandRunner.js +698 -18
- package/dist/orchestrator/src/cli/services/execRuntime.js +66 -1
- package/dist/orchestrator/src/cli/services/orchestratorAutoScoutEvidenceRecorder.js +71 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudBranchResolution.js +8 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudEnvironmentResolution.js +22 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudExecutionLifecycleShell.js +39 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudPromptBuilder.js +37 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudRouteFallbackContract.js +45 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudRouteShell.js +36 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudTargetExecutor.js +277 -0
- package/dist/orchestrator/src/cli/services/orchestratorControlPlaneLifecycle.js +98 -0
- package/dist/orchestrator/src/cli/services/orchestratorControlPlaneLifecycleShell.js +54 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionLifecycle.js +112 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionModePolicy.js +27 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteAdapterShell.js +59 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteDecisionShell.js +57 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteState.js +21 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouter.js +2 -0
- package/dist/orchestrator/src/cli/services/orchestratorLocalPipelineExecutor.js +149 -0
- package/dist/orchestrator/src/cli/services/orchestratorLocalRouteShell.js +63 -0
- package/dist/orchestrator/src/cli/services/orchestratorPlanShell.js +54 -0
- package/dist/orchestrator/src/cli/services/orchestratorPlanTargetTracker.js +16 -0
- package/dist/orchestrator/src/cli/services/orchestratorResumePreparationShell.js +84 -0
- package/dist/orchestrator/src/cli/services/orchestratorResumeTokenValidation.js +15 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleCompletion.js +31 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleExecutionRegistration.js +37 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleOrchestrationShell.js +83 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleTaskManagerShell.js +37 -0
- package/dist/orchestrator/src/cli/services/orchestratorRuntimeManifestMutation.js +20 -0
- package/dist/orchestrator/src/cli/services/orchestratorStartPreparationShell.js +56 -0
- package/dist/orchestrator/src/cli/services/orchestratorStatusShell.js +70 -0
- package/dist/orchestrator/src/cli/services/pipelineResolver.js +7 -3
- package/dist/orchestrator/src/cli/services/plannerMemory.js +119 -0
- package/dist/orchestrator/src/cli/services/runPreparation.js +7 -3
- package/dist/orchestrator/src/cli/services/runSummaryWriter.js +9 -0
- package/dist/orchestrator/src/cli/setupBootstrapShell.js +114 -0
- package/dist/orchestrator/src/cli/setupCliShell.js +51 -0
- package/dist/orchestrator/src/cli/skillsCliShell.js +56 -0
- package/dist/orchestrator/src/cli/startCliRequestShell.js +53 -0
- package/dist/orchestrator/src/cli/startCliShell.js +68 -0
- package/dist/orchestrator/src/cli/statusCliShell.js +22 -0
- package/dist/orchestrator/src/cli/utils/authProvenanceFingerprint.js +27 -0
- package/dist/orchestrator/src/cli/utils/cloudPreflight.js +285 -7
- package/dist/orchestrator/src/cli/utils/codexFeatures.js +60 -0
- package/dist/orchestrator/src/cli/utils/delegationConfigParser.js +250 -0
- package/dist/orchestrator/src/cli/utils/delegationMcpHealth.js +1382 -0
- package/dist/orchestrator/src/cli/utils/devtools.js +2 -54
- package/dist/orchestrator/src/cli/utils/mcpServerEntry.js +53 -0
- package/dist/orchestrator/src/cli/utils/packageProgramResolver.js +151 -0
- package/dist/orchestrator/src/cli/utils/providerOverrideEnv.js +71 -0
- package/dist/orchestrator/src/cli/utils/trailingJsonObject.js +59 -0
- package/dist/orchestrator/src/learning/crystalizer.js +2 -2
- package/dist/orchestrator/src/manager.js +74 -4
- package/dist/orchestrator/src/persistence/ExperienceStore.js +233 -49
- package/dist/orchestrator/src/persistence/TaskStateStore.js +6 -6
- package/dist/orchestrator/src/persistence/lockFile.js +70 -4
- package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +39 -0
- package/dist/orchestrator/src/sync/createCloudSyncWorker.js +3 -2
- package/dist/orchestrator/src/utils/atomicWrite.js +17 -2
- package/dist/packages/orchestrator/src/exec/unified-exec.js +99 -6
- package/dist/packages/orchestrator/src/instructions/promptPacks.js +150 -19
- package/dist/packages/sdk-node/src/orchestrator.js +137 -13
- package/dist/packages/shared/config/designConfig.js +8 -1
- package/dist/packages/shared/streams/stdio.js +1 -1
- package/dist/scripts/design/pipeline/permit.js +15 -0
- package/dist/scripts/lib/docs-catalog.js +399 -0
- package/dist/scripts/lib/docs-helpers.js +87 -5
- package/dist/scripts/lib/pr-watch-merge.js +1088 -80
- package/dist/scripts/lib/provider-run-contract.js +26 -0
- package/dist/scripts/lib/review-command-intent-classification.js +532 -0
- package/dist/scripts/lib/review-command-probe-classification.js +385 -0
- package/dist/scripts/lib/review-execution-boundary-preflight.js +279 -0
- package/dist/scripts/lib/review-execution-runtime.js +753 -0
- package/dist/scripts/lib/review-execution-state.js +1144 -0
- package/dist/scripts/lib/review-execution-telemetry.js +215 -0
- package/dist/scripts/lib/review-inspection-target-parsing.js +78 -0
- package/dist/scripts/lib/review-launch-attempt.js +601 -0
- package/dist/scripts/lib/review-meta-surface-boundary-analysis.js +300 -0
- package/dist/scripts/lib/review-meta-surface-normalization.js +746 -0
- package/dist/scripts/lib/review-non-interactive-handoff.js +61 -0
- package/dist/scripts/lib/review-prompt-context.js +376 -0
- package/dist/scripts/lib/review-scope-advisory.js +286 -0
- package/dist/scripts/lib/review-scope-paths.js +123 -0
- package/dist/scripts/lib/review-shell-command-parser.js +389 -0
- package/dist/scripts/lib/review-shell-env-interpreter.js +340 -0
- package/dist/scripts/lib/run-manifests.js +192 -36
- package/dist/scripts/lib/spark-policy-classifier.js +593 -0
- package/dist/scripts/run-review.js +507 -1777
- package/docs/README.md +43 -20
- package/docs/book/README.md +19 -0
- package/docs/book/codex-cli-0124-adoption.md +68 -0
- package/docs/book/local-hook-impact.md +73 -0
- package/docs/book/operations.md +60 -0
- package/docs/book/public-posture.md +34 -0
- package/docs/book/setup.md +91 -0
- package/docs/book/skills.md +11 -0
- package/docs/guides/codex-version-policy.md +104 -0
- package/docs/public/downstream-setup.md +113 -0
- package/docs/public/provider-onboarding.md +173 -0
- package/package.json +23 -10
- package/plugins/codex-orchestrator/.codex-plugin/plugin.json +30 -0
- package/plugins/codex-orchestrator/.mcp.json +13 -0
- package/plugins/codex-orchestrator/launcher.mjs +361 -0
- package/schemas/manifest.json +411 -0
- package/skills/README.md +26 -0
- package/skills/collab-subagents-first/SKILL.md +1 -1
- package/skills/delegation-usage/DELEGATION_GUIDE.md +30 -12
- package/skills/delegation-usage/SKILL.md +25 -14
- package/skills/land/SKILL.md +77 -0
- package/skills/linear/SKILL.md +255 -0
- package/skills/release/SKILL.md +47 -3
- package/skills/standalone-review/SKILL.md +6 -1
- package/templates/README.md +4 -2
- package/templates/codex/.codex/agents/awaiter-high.toml +2 -2
- package/templates/codex/.codex/agents/worker-complex.toml +1 -1
- package/templates/codex/.codex/config.toml +3 -4
- package/templates/codex/.codex/providers/README.md +13 -0
- package/templates/codex/.codex/providers/control.example.json +18 -0
- package/templates/codex/.codex/providers/provider.env.example +15 -0
- package/templates/codex/AGENTS.md +15 -8
- package/templates/codex/mcp-client.json +5 -1
- package/docs/assets/setup.gif +0 -0
|
@@ -0,0 +1,1068 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { readProviderLinearParallelizationSnapshots, summarizeProviderLinearAuditPath } from './providerLinearWorkflowAudit.js';
|
|
4
|
+
const DEFAULT_THRESHOLDS = {
|
|
5
|
+
staleRefreshAfterMs: 5 * 60 * 1000,
|
|
6
|
+
staleHeartbeatAfterMs: 10 * 60 * 1000,
|
|
7
|
+
staleRetryAfterMs: 10 * 60 * 1000,
|
|
8
|
+
staleClaimQueueAfterMs: 30 * 60 * 1000,
|
|
9
|
+
claimToStartDegradedAfterMs: 5 * 60 * 1000,
|
|
10
|
+
startToHeartbeatDegradedAfterMs: 2 * 60 * 1000,
|
|
11
|
+
linearHeadroomLowRatio: 0.05,
|
|
12
|
+
childLaneCap: 2
|
|
13
|
+
};
|
|
14
|
+
const PROVIDER_LINEAR_CHILD_LANE_IN_FLIGHT_STALE_MS = 30 * 60 * 1000;
|
|
15
|
+
const STATUS_DATASET_NAMES = new Set([
|
|
16
|
+
'co-status-dataset.json',
|
|
17
|
+
'operator-dashboard-dataset.json',
|
|
18
|
+
'operator-dashboard.json',
|
|
19
|
+
'status-dataset.json',
|
|
20
|
+
'ui-data.json'
|
|
21
|
+
]);
|
|
22
|
+
const POLLING_HEALTH_NAMES = new Set([
|
|
23
|
+
'provider-polling-health.json',
|
|
24
|
+
'control-polling-health.json',
|
|
25
|
+
'polling-health.json'
|
|
26
|
+
]);
|
|
27
|
+
const LINEAR_BUDGET_NAMES = new Set([
|
|
28
|
+
'linear-budget-state.json',
|
|
29
|
+
'linear-budget-status.json',
|
|
30
|
+
'linear-budget.json'
|
|
31
|
+
]);
|
|
32
|
+
const SKIPPED_SCAN_DIRS = new Set([
|
|
33
|
+
'.git',
|
|
34
|
+
'.next',
|
|
35
|
+
'.turbo',
|
|
36
|
+
'dist',
|
|
37
|
+
'node_modules'
|
|
38
|
+
]);
|
|
39
|
+
const QUEUED_CLAIM_STATES = new Set([
|
|
40
|
+
'accepted',
|
|
41
|
+
'starting',
|
|
42
|
+
'resuming'
|
|
43
|
+
]);
|
|
44
|
+
const ACTIVE_CLAIM_STATES = new Set([
|
|
45
|
+
...QUEUED_CLAIM_STATES,
|
|
46
|
+
'running'
|
|
47
|
+
]);
|
|
48
|
+
const TERMINAL_MANIFEST_STATUSES = new Set([
|
|
49
|
+
'cancelled',
|
|
50
|
+
'canceled',
|
|
51
|
+
'completed',
|
|
52
|
+
'failed',
|
|
53
|
+
'succeeded'
|
|
54
|
+
]);
|
|
55
|
+
export async function evaluateProviderControlHostFreshnessGauge(options = {}) {
|
|
56
|
+
const thresholds = normalizeThresholds(options.thresholds);
|
|
57
|
+
const nowMs = normalizeNowMs(options.now);
|
|
58
|
+
const generatedAt = new Date(nowMs).toISOString();
|
|
59
|
+
const sources = await discoverProviderControlHostFreshnessGaugeSources(options);
|
|
60
|
+
const findings = [];
|
|
61
|
+
const artifacts = await readGaugeArtifacts(sources, findings);
|
|
62
|
+
const statusDataset = selectLatestJsonArtifact(artifacts.statusDatasets);
|
|
63
|
+
const intakeState = selectLatestJsonArtifact(artifacts.providerIntakeStates);
|
|
64
|
+
const manifests = artifacts.providerManifests;
|
|
65
|
+
const proofs = artifacts.providerProofs.map((proof) => ({
|
|
66
|
+
...proof,
|
|
67
|
+
manifest: findManifestForProof(proof, manifests)
|
|
68
|
+
}));
|
|
69
|
+
const pollingHealth = selectPollingHealthArtifact({
|
|
70
|
+
explicit: artifacts.pollingHealth,
|
|
71
|
+
intakeState,
|
|
72
|
+
statusDataset
|
|
73
|
+
});
|
|
74
|
+
const linearBudget = selectLinearBudgetArtifact({
|
|
75
|
+
explicit: artifacts.linearBudgetState,
|
|
76
|
+
pollingHealth,
|
|
77
|
+
statusDataset,
|
|
78
|
+
proofs
|
|
79
|
+
});
|
|
80
|
+
const metrics = {
|
|
81
|
+
claim_queue_age_ms: evaluateClaimQueueAge(intakeState, nowMs, thresholds, findings),
|
|
82
|
+
last_successful_refresh_age_ms: evaluateRefreshAge({ intakeState, pollingHealth, statusDataset }, nowMs, thresholds, findings),
|
|
83
|
+
polling_health: evaluatePollingHealth(pollingHealth, thresholds, findings),
|
|
84
|
+
claim_to_start_latency_ms: evaluateClaimToStartLatency(intakeState, manifests, nowMs, thresholds, findings),
|
|
85
|
+
start_to_first_heartbeat_latency_ms: evaluateStartToHeartbeatLatency(proofs, nowMs, thresholds, findings),
|
|
86
|
+
active_heartbeat_age_ms: evaluateActiveHeartbeatAge(intakeState, proofs, nowMs, thresholds, findings),
|
|
87
|
+
terminal_reconciliation_lag_ms: evaluateTerminalReconciliationLag(intakeState, proofs, nowMs, findings),
|
|
88
|
+
retry_backoff_age_ms: evaluateRetryBackoffAge({ intakeState, statusDataset }, nowMs, thresholds, findings),
|
|
89
|
+
child_lane_cap_pressure: evaluateChildLaneCapPressure(proofs, nowMs, thresholds, findings),
|
|
90
|
+
stale_source_verdict: buildUnknownVerdictMetric()
|
|
91
|
+
};
|
|
92
|
+
evaluateLinearBudget(linearBudget, thresholds, findings);
|
|
93
|
+
evaluateWorkerAuditHealth(artifacts.workerAudits, findings);
|
|
94
|
+
if (!hasCoreFreshnessSources(sources)) {
|
|
95
|
+
const auxiliarySourcePath = firstAuxiliarySourcePath(sources);
|
|
96
|
+
findings.push({
|
|
97
|
+
code: auxiliarySourcePath ? 'missing_core_freshness_artifacts' : 'no_provider_control_host_artifacts',
|
|
98
|
+
verdict: 'unknown',
|
|
99
|
+
message: auxiliarySourcePath
|
|
100
|
+
? 'Auxiliary provider/control-host artifacts were discovered, but no core freshness source was available.'
|
|
101
|
+
: 'No provider/control-host freshness artifacts were discovered.',
|
|
102
|
+
source_path: auxiliarySourcePath ?? sources.artifact_root,
|
|
103
|
+
source_field: null
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const verdict = resolveOverallVerdict(findings);
|
|
107
|
+
metrics.stale_source_verdict = {
|
|
108
|
+
value: verdict,
|
|
109
|
+
unit: null,
|
|
110
|
+
verdict,
|
|
111
|
+
source_path: firstStaleSourcePath(findings),
|
|
112
|
+
source_field: null,
|
|
113
|
+
reason: findings.length === 0 ? 'no stale or contradictory sources detected' : 'see findings'
|
|
114
|
+
};
|
|
115
|
+
const strictFailed = options.strict === true && (verdict === 'stale' || verdict === 'contradictory');
|
|
116
|
+
return {
|
|
117
|
+
schema_version: 1,
|
|
118
|
+
generated_at: generatedAt,
|
|
119
|
+
read_only: true,
|
|
120
|
+
verdict,
|
|
121
|
+
strict_failed: strictFailed,
|
|
122
|
+
thresholds,
|
|
123
|
+
sources,
|
|
124
|
+
metrics,
|
|
125
|
+
findings
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async function discoverProviderControlHostFreshnessGaugeSources(options) {
|
|
129
|
+
const artifactRoot = options.artifactRoot ? resolve(options.artifactRoot) : null;
|
|
130
|
+
const sources = {
|
|
131
|
+
artifact_root: artifactRoot,
|
|
132
|
+
provider_intake_state: normalizePathList(options.paths?.provider_intake_state),
|
|
133
|
+
provider_manifests: normalizePathList(options.paths?.provider_manifests),
|
|
134
|
+
provider_proofs: normalizePathList(options.paths?.provider_proofs),
|
|
135
|
+
worker_audit_jsonl: normalizePathList(options.paths?.worker_audit_jsonl),
|
|
136
|
+
control_endpoint_metadata: normalizePathList(options.paths?.control_endpoint_metadata),
|
|
137
|
+
status_datasets: normalizePathList(options.paths?.status_datasets),
|
|
138
|
+
polling_health: normalizePathList(options.paths?.polling_health),
|
|
139
|
+
linear_budget_state: normalizePathList(options.paths?.linear_budget_state)
|
|
140
|
+
};
|
|
141
|
+
if (!artifactRoot) {
|
|
142
|
+
return sources;
|
|
143
|
+
}
|
|
144
|
+
const discovered = await scanArtifactRoot(artifactRoot, options.maxDepth ?? 8);
|
|
145
|
+
appendUnique(sources.provider_intake_state, discovered.provider_intake_state);
|
|
146
|
+
appendUnique(sources.provider_manifests, discovered.provider_manifests);
|
|
147
|
+
appendUnique(sources.provider_proofs, discovered.provider_proofs);
|
|
148
|
+
appendUnique(sources.worker_audit_jsonl, discovered.worker_audit_jsonl);
|
|
149
|
+
appendUnique(sources.control_endpoint_metadata, discovered.control_endpoint_metadata);
|
|
150
|
+
appendUnique(sources.status_datasets, discovered.status_datasets);
|
|
151
|
+
appendUnique(sources.polling_health, discovered.polling_health);
|
|
152
|
+
appendUnique(sources.linear_budget_state, discovered.linear_budget_state);
|
|
153
|
+
return sources;
|
|
154
|
+
}
|
|
155
|
+
async function scanArtifactRoot(artifactRoot, maxDepth) {
|
|
156
|
+
const sources = {
|
|
157
|
+
provider_intake_state: [],
|
|
158
|
+
provider_manifests: [],
|
|
159
|
+
provider_proofs: [],
|
|
160
|
+
worker_audit_jsonl: [],
|
|
161
|
+
control_endpoint_metadata: [],
|
|
162
|
+
status_datasets: [],
|
|
163
|
+
polling_health: [],
|
|
164
|
+
linear_budget_state: []
|
|
165
|
+
};
|
|
166
|
+
await scanDirectory(artifactRoot, 0, Math.max(0, maxDepth), sources);
|
|
167
|
+
return sources;
|
|
168
|
+
}
|
|
169
|
+
async function scanDirectory(directory, depth, maxDepth, sources) {
|
|
170
|
+
let entries;
|
|
171
|
+
try {
|
|
172
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
for (const entry of entries) {
|
|
178
|
+
if (entry.isDirectory()) {
|
|
179
|
+
if (depth >= maxDepth || SKIPPED_SCAN_DIRS.has(entry.name)) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
await scanDirectory(resolve(directory, entry.name), depth + 1, maxDepth, sources);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (!entry.isFile()) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const path = resolve(directory, entry.name);
|
|
189
|
+
switch (entry.name) {
|
|
190
|
+
case 'provider-intake-state.json':
|
|
191
|
+
sources.provider_intake_state.push(path);
|
|
192
|
+
break;
|
|
193
|
+
case 'manifest.json':
|
|
194
|
+
sources.provider_manifests.push(path);
|
|
195
|
+
break;
|
|
196
|
+
case 'provider-linear-worker-proof.json':
|
|
197
|
+
sources.provider_proofs.push(path);
|
|
198
|
+
break;
|
|
199
|
+
case 'provider-linear-worker-linear-audit.jsonl':
|
|
200
|
+
sources.worker_audit_jsonl.push(path);
|
|
201
|
+
break;
|
|
202
|
+
case 'control_endpoint.json':
|
|
203
|
+
sources.control_endpoint_metadata.push(path);
|
|
204
|
+
break;
|
|
205
|
+
default:
|
|
206
|
+
if (STATUS_DATASET_NAMES.has(entry.name)) {
|
|
207
|
+
sources.status_datasets.push(path);
|
|
208
|
+
}
|
|
209
|
+
else if (POLLING_HEALTH_NAMES.has(entry.name)) {
|
|
210
|
+
sources.polling_health.push(path);
|
|
211
|
+
}
|
|
212
|
+
else if (LINEAR_BUDGET_NAMES.has(entry.name)) {
|
|
213
|
+
sources.linear_budget_state.push(path);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function readGaugeArtifacts(sources, findings) {
|
|
219
|
+
const providerIntakeStates = await readJsonArtifacts(sources.provider_intake_state, findings);
|
|
220
|
+
const latestProviderIntakeState = selectLatestJsonArtifact(providerIntakeStates);
|
|
221
|
+
const claimLinkedSources = await discoverClaimLinkedRunArtifactSources(latestProviderIntakeState ? [latestProviderIntakeState] : [], sources.artifact_root);
|
|
222
|
+
appendUnique(sources.provider_manifests, claimLinkedSources.provider_manifests);
|
|
223
|
+
appendUnique(sources.provider_proofs, claimLinkedSources.provider_proofs);
|
|
224
|
+
const [rawManifests, rawProofs, workerAudits, controlEndpointMetadata, pollingHealth, statusDatasets, linearBudgetState] = await Promise.all([
|
|
225
|
+
readJsonArtifacts(sources.provider_manifests, findings),
|
|
226
|
+
readJsonArtifacts(sources.provider_proofs, findings),
|
|
227
|
+
readWorkerAuditArtifacts(sources.worker_audit_jsonl, findings),
|
|
228
|
+
readJsonArtifacts(sources.control_endpoint_metadata, findings),
|
|
229
|
+
readJsonArtifacts(sources.polling_health, findings),
|
|
230
|
+
readJsonArtifacts(sources.status_datasets, findings),
|
|
231
|
+
readJsonArtifacts(sources.linear_budget_state, findings)
|
|
232
|
+
]);
|
|
233
|
+
const providerManifests = rawManifests.flatMap((artifact) => {
|
|
234
|
+
const value = asRecord(artifact.value);
|
|
235
|
+
return value ? [{ path: artifact.path, runDir: dirname(artifact.path), value }] : [];
|
|
236
|
+
});
|
|
237
|
+
const providerProofs = rawProofs.flatMap((artifact) => {
|
|
238
|
+
const value = asRecord(artifact.value);
|
|
239
|
+
return value ? [{ path: artifact.path, runDir: dirname(artifact.path), value, manifest: null }] : [];
|
|
240
|
+
});
|
|
241
|
+
return {
|
|
242
|
+
providerIntakeStates,
|
|
243
|
+
providerManifests,
|
|
244
|
+
providerProofs,
|
|
245
|
+
workerAudits,
|
|
246
|
+
controlEndpointMetadata,
|
|
247
|
+
pollingHealth,
|
|
248
|
+
statusDatasets,
|
|
249
|
+
linearBudgetState
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
async function discoverClaimLinkedRunArtifactSources(providerIntakeStates, artifactRoot) {
|
|
253
|
+
const provider_manifests = [];
|
|
254
|
+
const provider_proofs = [];
|
|
255
|
+
for (const artifact of providerIntakeStates) {
|
|
256
|
+
const claims = asRecord(artifact.value)?.claims;
|
|
257
|
+
if (!Array.isArray(claims)) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
for (const rawClaim of claims) {
|
|
261
|
+
const claim = asRecord(rawClaim);
|
|
262
|
+
if (!claim || !ACTIVE_CLAIM_STATES.has(readState(claim))) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const manifestPath = normalizeOptionalString(claim?.run_manifest_path);
|
|
266
|
+
if (!manifestPath) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
const resolvedManifestPath = await firstReadablePath(resolveClaimLinkedRunManifestPathCandidates({
|
|
270
|
+
artifact,
|
|
271
|
+
artifactRoot,
|
|
272
|
+
manifestPath
|
|
273
|
+
}));
|
|
274
|
+
if (!resolvedManifestPath) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
provider_manifests.push(resolvedManifestPath);
|
|
278
|
+
const proofPath = resolve(dirname(resolvedManifestPath), 'provider-linear-worker-proof.json');
|
|
279
|
+
if (await isReadableFile(proofPath)) {
|
|
280
|
+
provider_proofs.push(proofPath);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
provider_manifests,
|
|
286
|
+
provider_proofs
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function resolveClaimLinkedRunManifestPathCandidates(input) {
|
|
290
|
+
if (isAbsolute(input.manifestPath)) {
|
|
291
|
+
return [input.manifestPath];
|
|
292
|
+
}
|
|
293
|
+
const bases = [
|
|
294
|
+
dirname(input.artifact.path),
|
|
295
|
+
input.artifactRoot,
|
|
296
|
+
findRepoRootFromRunsPath(input.artifact.path),
|
|
297
|
+
input.artifactRoot ? findRepoRootFromRunsPath(input.artifactRoot) : null
|
|
298
|
+
];
|
|
299
|
+
return Array.from(new Set(bases.flatMap((base) => (base ? [resolve(base, input.manifestPath)] : []))));
|
|
300
|
+
}
|
|
301
|
+
function findRepoRootFromRunsPath(path) {
|
|
302
|
+
const parts = resolve(path).split(sep);
|
|
303
|
+
const runsIndex = parts.lastIndexOf('.runs');
|
|
304
|
+
if (runsIndex <= 0) {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
return parts.slice(0, runsIndex).join(sep) || sep;
|
|
308
|
+
}
|
|
309
|
+
async function firstReadablePath(candidates) {
|
|
310
|
+
for (const candidate of candidates) {
|
|
311
|
+
if (await isReadableFile(candidate)) {
|
|
312
|
+
return candidate;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
async function isReadableFile(path) {
|
|
318
|
+
try {
|
|
319
|
+
return (await stat(path)).isFile();
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async function readJsonArtifacts(paths, findings) {
|
|
326
|
+
const artifacts = [];
|
|
327
|
+
for (const path of paths) {
|
|
328
|
+
try {
|
|
329
|
+
const raw = await readFile(path, 'utf8');
|
|
330
|
+
artifacts.push({ path, value: JSON.parse(raw) });
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
findings.push({
|
|
334
|
+
code: 'artifact_unreadable',
|
|
335
|
+
verdict: 'unknown',
|
|
336
|
+
message: `Artifact could not be read or parsed: ${describeError(error)}`,
|
|
337
|
+
source_path: path,
|
|
338
|
+
source_field: null
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return artifacts;
|
|
343
|
+
}
|
|
344
|
+
async function readWorkerAuditArtifacts(paths, findings) {
|
|
345
|
+
const artifacts = [];
|
|
346
|
+
for (const path of paths) {
|
|
347
|
+
let raw;
|
|
348
|
+
try {
|
|
349
|
+
raw = await readFile(path, 'utf8');
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
findings.push({
|
|
353
|
+
code: 'worker_audit_unreadable',
|
|
354
|
+
verdict: 'unknown',
|
|
355
|
+
message: `Worker audit JSONL could not be read: ${describeError(error)}`,
|
|
356
|
+
source_path: path,
|
|
357
|
+
source_field: null
|
|
358
|
+
});
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const lines = raw.split(/\r?\n/u).filter((line) => line.trim().length > 0);
|
|
362
|
+
const malformedLineCount = lines.filter((line) => {
|
|
363
|
+
try {
|
|
364
|
+
JSON.parse(line);
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
}).length;
|
|
371
|
+
if (malformedLineCount > 0) {
|
|
372
|
+
findings.push({
|
|
373
|
+
code: 'worker_audit_jsonl_malformed',
|
|
374
|
+
verdict: 'unknown',
|
|
375
|
+
message: `Worker audit JSONL has ${malformedLineCount} malformed line(s).`,
|
|
376
|
+
source_path: path,
|
|
377
|
+
source_field: null
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
artifacts.push({
|
|
381
|
+
path,
|
|
382
|
+
summary: await summarizeProviderLinearAuditPath(path),
|
|
383
|
+
lineCount: lines.length,
|
|
384
|
+
malformedLineCount
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
return artifacts;
|
|
388
|
+
}
|
|
389
|
+
function evaluateClaimQueueAge(intakeState, nowMs, thresholds, findings) {
|
|
390
|
+
const claims = collectClaims(intakeState);
|
|
391
|
+
const queuedClaims = claims.filter((claim) => QUEUED_CLAIM_STATES.has(readState(claim)));
|
|
392
|
+
const candidates = queuedClaims.flatMap((claim) => {
|
|
393
|
+
const acceptedAt = timestampMs(claim.accepted_at ?? claim.updated_at);
|
|
394
|
+
return acceptedAt === null ? [] : [{ claim, acceptedAt, ageMs: Math.max(0, nowMs - acceptedAt) }];
|
|
395
|
+
});
|
|
396
|
+
if (candidates.length === 0) {
|
|
397
|
+
return metric(null, 'ms', 'unknown', intakeState?.path ?? null, 'claims[].accepted_at', 'no queued claims observed');
|
|
398
|
+
}
|
|
399
|
+
const oldest = candidates.reduce((winner, candidate) => candidate.ageMs > winner.ageMs ? candidate : winner);
|
|
400
|
+
if (oldest.ageMs > thresholds.staleClaimQueueAfterMs) {
|
|
401
|
+
findings.push({
|
|
402
|
+
code: 'claim_queue_stale',
|
|
403
|
+
verdict: 'stale',
|
|
404
|
+
message: `Oldest queued provider claim is ${oldest.ageMs}ms old.`,
|
|
405
|
+
source_path: intakeState?.path ?? null,
|
|
406
|
+
source_field: 'claims[].accepted_at'
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
return metric(oldest.ageMs, 'ms', oldest.ageMs > thresholds.staleClaimQueueAfterMs ? 'stale' : 'healthy', intakeState?.path ?? null, 'claims[].accepted_at', null);
|
|
410
|
+
}
|
|
411
|
+
function evaluateRefreshAge(input, nowMs, thresholds, findings) {
|
|
412
|
+
const observedRefreshCandidates = [
|
|
413
|
+
timestampCandidate(input.pollingHealth, 'last_success_at'),
|
|
414
|
+
timestampCandidate(input.intakeState, 'polling.last_success_at'),
|
|
415
|
+
timestampCandidate(input.statusDataset, 'polling.last_success_at')
|
|
416
|
+
].filter((candidate) => candidate !== null);
|
|
417
|
+
const candidates = observedRefreshCandidates.length > 0
|
|
418
|
+
? observedRefreshCandidates
|
|
419
|
+
: [timestampCandidate(input.statusDataset, 'generated_at')].filter((candidate) => candidate !== null);
|
|
420
|
+
const latest = selectLatestTimestampCandidate(candidates);
|
|
421
|
+
if (!latest) {
|
|
422
|
+
const missingRefreshSource = firstRefreshTimestampSource(input);
|
|
423
|
+
if (missingRefreshSource) {
|
|
424
|
+
findings.push({
|
|
425
|
+
code: 'refresh_timestamp_missing',
|
|
426
|
+
verdict: 'unknown',
|
|
427
|
+
message: 'No successful provider/control-host refresh timestamp was observed.',
|
|
428
|
+
source_path: missingRefreshSource.path,
|
|
429
|
+
source_field: missingRefreshSource.field
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
return metric(null, 'ms', 'unknown', missingRefreshSource?.path ?? null, missingRefreshSource?.field ?? null, 'no refresh timestamp observed');
|
|
433
|
+
}
|
|
434
|
+
const age = Math.max(0, nowMs - latest.timestampMs);
|
|
435
|
+
if (age > thresholds.staleRefreshAfterMs) {
|
|
436
|
+
findings.push({
|
|
437
|
+
code: 'stale_refresh',
|
|
438
|
+
verdict: 'stale',
|
|
439
|
+
message: `Last successful provider/control-host refresh is ${age}ms old.`,
|
|
440
|
+
source_path: latest.path,
|
|
441
|
+
source_field: latest.field
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
return metric(age, 'ms', age > thresholds.staleRefreshAfterMs ? 'stale' : 'healthy', latest.path, latest.field, null);
|
|
445
|
+
}
|
|
446
|
+
function evaluatePollingHealth(pollingHealth, _thresholds, findings) {
|
|
447
|
+
const polling = asRecord(pollingHealth?.value);
|
|
448
|
+
if (!polling) {
|
|
449
|
+
return metric(null, null, 'unknown', null, null, 'no polling health artifact observed');
|
|
450
|
+
}
|
|
451
|
+
if (polling.stuck === true || polling.restart_required === true) {
|
|
452
|
+
findings.push({
|
|
453
|
+
code: 'polling_stuck',
|
|
454
|
+
verdict: 'stale',
|
|
455
|
+
message: 'Provider polling health reports a stuck/restart-required state.',
|
|
456
|
+
source_path: pollingHealth?.path ?? null,
|
|
457
|
+
source_field: polling.stuck === true ? 'stuck' : 'restart_required'
|
|
458
|
+
});
|
|
459
|
+
return metric('stuck', null, 'stale', pollingHealth?.path ?? null, null, normalizeOptionalString(polling.reason));
|
|
460
|
+
}
|
|
461
|
+
if (polling.last_error && !polling.last_success_at) {
|
|
462
|
+
findings.push({
|
|
463
|
+
code: 'polling_error_without_success',
|
|
464
|
+
verdict: 'degraded',
|
|
465
|
+
message: 'Provider polling reports an error and no successful refresh.',
|
|
466
|
+
source_path: pollingHealth?.path ?? null,
|
|
467
|
+
source_field: 'last_error'
|
|
468
|
+
});
|
|
469
|
+
return metric('error', null, 'degraded', pollingHealth?.path ?? null, 'last_error', normalizeOptionalString(polling.last_error));
|
|
470
|
+
}
|
|
471
|
+
return metric('ok', null, 'healthy', pollingHealth?.path ?? null, null, null);
|
|
472
|
+
}
|
|
473
|
+
function evaluateClaimToStartLatency(intakeState, manifests, _nowMs, thresholds, findings) {
|
|
474
|
+
const claims = collectClaims(intakeState).filter(isActiveClaim);
|
|
475
|
+
const candidates = claims.flatMap((claim) => {
|
|
476
|
+
const acceptedAt = timestampMs(claim.accepted_at);
|
|
477
|
+
const runId = normalizeOptionalString(claim.run_id);
|
|
478
|
+
const manifest = runId ? findLatestManifestForRunId(manifests, runId) : null;
|
|
479
|
+
const startedAt = timestampMs(claim.launch_started_at) ??
|
|
480
|
+
timestampMs(manifest?.value.started_at) ??
|
|
481
|
+
timestampMs(manifest?.value.startedAt);
|
|
482
|
+
return acceptedAt !== null && startedAt !== null
|
|
483
|
+
? [{ latencyMs: Math.max(0, startedAt - acceptedAt), manifest }]
|
|
484
|
+
: [];
|
|
485
|
+
});
|
|
486
|
+
if (candidates.length === 0) {
|
|
487
|
+
return metric(null, 'ms', 'unknown', intakeState?.path ?? null, 'claims[].launch_started_at', 'no claim-to-start pair observed');
|
|
488
|
+
}
|
|
489
|
+
const slowest = candidates.reduce((winner, candidate) => candidate.latencyMs > winner.latencyMs ? candidate : winner);
|
|
490
|
+
if (slowest.latencyMs > thresholds.claimToStartDegradedAfterMs) {
|
|
491
|
+
findings.push({
|
|
492
|
+
code: 'claim_to_start_latency_degraded',
|
|
493
|
+
verdict: 'degraded',
|
|
494
|
+
message: `Provider claim-to-start latency is ${slowest.latencyMs}ms.`,
|
|
495
|
+
source_path: slowest.manifest?.path ?? intakeState?.path ?? null,
|
|
496
|
+
source_field: 'claims[].accepted_at'
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
return metric(slowest.latencyMs, 'ms', slowest.latencyMs > thresholds.claimToStartDegradedAfterMs ? 'degraded' : 'healthy', slowest.manifest?.path ?? intakeState?.path ?? null, 'claims[].accepted_at', null);
|
|
500
|
+
}
|
|
501
|
+
function evaluateStartToHeartbeatLatency(proofs, _nowMs, thresholds, findings) {
|
|
502
|
+
const candidates = proofs.flatMap((proof) => {
|
|
503
|
+
if (!isActiveProof(proof)) {
|
|
504
|
+
return [];
|
|
505
|
+
}
|
|
506
|
+
const startedAt = timestampMs(proof.value.attempt_started_at) ??
|
|
507
|
+
timestampMs(proof.value.current_turn_started_at) ??
|
|
508
|
+
timestampMs(proof.manifest?.value.started_at) ??
|
|
509
|
+
timestampMs(proof.manifest?.value.startedAt);
|
|
510
|
+
const heartbeatAt = firstHeartbeatTimestampMs(proof);
|
|
511
|
+
return startedAt !== null && heartbeatAt !== null
|
|
512
|
+
? [{ latencyMs: Math.max(0, heartbeatAt - startedAt), proof }]
|
|
513
|
+
: [];
|
|
514
|
+
});
|
|
515
|
+
if (candidates.length === 0) {
|
|
516
|
+
return metric(null, 'ms', 'unknown', null, null, 'no start-to-heartbeat pair observed');
|
|
517
|
+
}
|
|
518
|
+
const slowest = candidates.reduce((winner, candidate) => candidate.latencyMs > winner.latencyMs ? candidate : winner);
|
|
519
|
+
if (slowest.latencyMs > thresholds.startToHeartbeatDegradedAfterMs) {
|
|
520
|
+
findings.push({
|
|
521
|
+
code: 'start_to_first_heartbeat_latency_degraded',
|
|
522
|
+
verdict: 'degraded',
|
|
523
|
+
message: `Provider start-to-first-heartbeat latency is ${slowest.latencyMs}ms.`,
|
|
524
|
+
source_path: slowest.proof.path,
|
|
525
|
+
source_field: 'first_heartbeat_at'
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
return metric(slowest.latencyMs, 'ms', slowest.latencyMs > thresholds.startToHeartbeatDegradedAfterMs ? 'degraded' : 'healthy', slowest.proof.path, 'first_heartbeat_at', null);
|
|
529
|
+
}
|
|
530
|
+
function evaluateActiveHeartbeatAge(intakeState, proofs, nowMs, thresholds, findings) {
|
|
531
|
+
const activeProofs = proofs.filter(isActiveProof);
|
|
532
|
+
const missingProofClaims = collectClaims(intakeState)
|
|
533
|
+
.filter((claim) => readState(claim) === 'running')
|
|
534
|
+
.filter((claim) => {
|
|
535
|
+
const runId = normalizeOptionalString(claim.run_id);
|
|
536
|
+
return !runId || !activeProofs.some((proof) => resolveProofRunId(proof) === runId);
|
|
537
|
+
});
|
|
538
|
+
if (missingProofClaims.length > 0) {
|
|
539
|
+
findings.push({
|
|
540
|
+
code: 'active_worker_proof_missing',
|
|
541
|
+
verdict: 'unknown',
|
|
542
|
+
message: 'Provider intake has a running claim without matching active provider-worker proof evidence.',
|
|
543
|
+
source_path: intakeState?.path ?? null,
|
|
544
|
+
source_field: 'claims[].run_id'
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
const candidates = activeProofs.flatMap((proof) => {
|
|
548
|
+
if (!isActiveProof(proof)) {
|
|
549
|
+
return [];
|
|
550
|
+
}
|
|
551
|
+
const heartbeatAt = latestTimestampMs(timestampMs(proof.value.updated_at), timestampMs(proof.value.last_event_at), firstTimestampMs(proof.value.current_turn_activity, 'recorded_at'), timestampMs(proof.manifest?.value.heartbeat_at), timestampMs(proof.manifest?.value.updated_at));
|
|
552
|
+
return heartbeatAt === null ? [] : [{ ageMs: Math.max(0, nowMs - heartbeatAt), proof }];
|
|
553
|
+
});
|
|
554
|
+
if (candidates.length === 0) {
|
|
555
|
+
return metric(null, 'ms', 'unknown', missingProofClaims.length > 0 ? intakeState?.path ?? null : null, missingProofClaims.length > 0 ? 'claims[].run_id' : null, missingProofClaims.length > 0
|
|
556
|
+
? 'running claim has no matching active provider proof'
|
|
557
|
+
: 'no active provider proof observed');
|
|
558
|
+
}
|
|
559
|
+
const stalest = candidates.reduce((winner, candidate) => candidate.ageMs > winner.ageMs ? candidate : winner);
|
|
560
|
+
if (stalest.ageMs > thresholds.staleHeartbeatAfterMs) {
|
|
561
|
+
findings.push({
|
|
562
|
+
code: 'active_heartbeat_stale',
|
|
563
|
+
verdict: 'stale',
|
|
564
|
+
message: `Active provider worker heartbeat is ${stalest.ageMs}ms old.`,
|
|
565
|
+
source_path: stalest.proof.path,
|
|
566
|
+
source_field: 'updated_at'
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
return metric(stalest.ageMs, 'ms', stalest.ageMs > thresholds.staleHeartbeatAfterMs ? 'stale' : 'healthy', stalest.proof.path, 'updated_at', null);
|
|
570
|
+
}
|
|
571
|
+
function evaluateTerminalReconciliationLag(intakeState, proofs, nowMs, findings) {
|
|
572
|
+
const claims = collectClaims(intakeState);
|
|
573
|
+
const activeClaims = claims.filter((claim) => ACTIVE_CLAIM_STATES.has(readState(claim)));
|
|
574
|
+
const unreconcilableTerminalProofs = [];
|
|
575
|
+
const candidates = proofs.flatMap((proof) => {
|
|
576
|
+
if (!isTerminalProof(proof)) {
|
|
577
|
+
return [];
|
|
578
|
+
}
|
|
579
|
+
const runId = normalizeOptionalString(proof.manifest?.value.run_id) ?? normalizeOptionalString(proof.value.run_id);
|
|
580
|
+
if (!runId) {
|
|
581
|
+
if (activeClaims.length > 0) {
|
|
582
|
+
unreconcilableTerminalProofs.push(proof);
|
|
583
|
+
findings.push({
|
|
584
|
+
code: 'terminal_proof_missing_run_id',
|
|
585
|
+
verdict: 'unknown',
|
|
586
|
+
message: 'Terminal provider proof/manifest cannot be reconciled against active claims because run_id is missing.',
|
|
587
|
+
source_path: proof.path,
|
|
588
|
+
source_field: 'run_id'
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
return [];
|
|
592
|
+
}
|
|
593
|
+
const matchingClaim = activeClaims.find((claim) => {
|
|
594
|
+
const claimRunId = normalizeOptionalString(claim.run_id);
|
|
595
|
+
return claimRunId === runId;
|
|
596
|
+
});
|
|
597
|
+
if (!matchingClaim) {
|
|
598
|
+
return [];
|
|
599
|
+
}
|
|
600
|
+
const terminalAt = latestTimestampMs(timestampMs(proof.manifest?.value.completed_at), timestampMs(proof.manifest?.value.updated_at), timestampMs(proof.value.updated_at), timestampMs(proof.value.last_event_at));
|
|
601
|
+
return [{ lagMs: terminalAt === null ? null : Math.max(0, nowMs - terminalAt), proof }];
|
|
602
|
+
});
|
|
603
|
+
if (candidates.length === 0) {
|
|
604
|
+
const unreconcilableTerminalProof = unreconcilableTerminalProofs[0];
|
|
605
|
+
if (unreconcilableTerminalProof) {
|
|
606
|
+
return metric(null, 'ms', 'unknown', unreconcilableTerminalProof.path, 'run_id', 'terminal proof/manifest missing run_id while active claims exist');
|
|
607
|
+
}
|
|
608
|
+
return metric(0, 'ms', 'healthy', intakeState?.path ?? null, null, null);
|
|
609
|
+
}
|
|
610
|
+
const worst = candidates.reduce((winner, candidate) => (candidate.lagMs ?? 0) > (winner.lagMs ?? 0) ? candidate : winner);
|
|
611
|
+
findings.push({
|
|
612
|
+
code: 'terminal_proof_with_active_claim',
|
|
613
|
+
verdict: 'contradictory',
|
|
614
|
+
message: worst.lagMs === null
|
|
615
|
+
? 'Terminal provider proof/manifest still has an active intake claim; terminal timestamp is unavailable.'
|
|
616
|
+
: `Terminal provider proof/manifest still has an active intake claim after ${worst.lagMs}ms.`,
|
|
617
|
+
source_path: worst.proof.path,
|
|
618
|
+
source_field: 'owner_status'
|
|
619
|
+
});
|
|
620
|
+
return metric(worst.lagMs, 'ms', 'contradictory', worst.proof.path, 'owner_status', 'terminal proof/manifest with active claim');
|
|
621
|
+
}
|
|
622
|
+
function evaluateRetryBackoffAge(input, nowMs, thresholds, findings) {
|
|
623
|
+
const retryCandidates = [
|
|
624
|
+
...collectClaims(input.intakeState)
|
|
625
|
+
.filter((claim) => claim.retry_queued === true || timestampMs(claim.retry_due_at) !== null)
|
|
626
|
+
.flatMap((claim) => {
|
|
627
|
+
const dueAt = timestampMs(claim.retry_due_at);
|
|
628
|
+
return dueAt === null ? [] : [{ dueAt, sourcePath: input.intakeState?.path ?? null, field: 'claims[].retry_due_at' }];
|
|
629
|
+
}),
|
|
630
|
+
...collectStatusRetrying(input.statusDataset).flatMap((retry) => {
|
|
631
|
+
const dueAt = timestampMs(retry.due_at);
|
|
632
|
+
return dueAt === null ? [] : [{ dueAt, sourcePath: input.statusDataset?.path ?? null, field: 'retrying[].due_at' }];
|
|
633
|
+
})
|
|
634
|
+
];
|
|
635
|
+
if (retryCandidates.length === 0) {
|
|
636
|
+
return metric(null, 'ms', 'unknown', null, null, 'no retry/backoff evidence observed');
|
|
637
|
+
}
|
|
638
|
+
const oldest = retryCandidates.reduce((winner, candidate) => candidate.dueAt < winner.dueAt ? candidate : winner);
|
|
639
|
+
const overdueAge = Math.max(0, nowMs - oldest.dueAt);
|
|
640
|
+
if (overdueAge > thresholds.staleRetryAfterMs) {
|
|
641
|
+
findings.push({
|
|
642
|
+
code: 'retry_queue_stale',
|
|
643
|
+
verdict: 'stale',
|
|
644
|
+
message: `Retry/backoff queue is overdue by ${overdueAge}ms.`,
|
|
645
|
+
source_path: oldest.sourcePath,
|
|
646
|
+
source_field: oldest.field
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
return metric(overdueAge, 'ms', overdueAge > thresholds.staleRetryAfterMs ? 'stale' : 'healthy', oldest.sourcePath, oldest.field, null);
|
|
650
|
+
}
|
|
651
|
+
function evaluateChildLaneCapPressure(proofs, nowMs, thresholds, findings) {
|
|
652
|
+
const activeProofs = proofs.filter(isActiveProof);
|
|
653
|
+
const lanes = activeProofs.flatMap((proof) => collectArrayRecords(proof.value.child_lanes).map((lane) => ({ lane, proof })));
|
|
654
|
+
const activeByParent = new Map();
|
|
655
|
+
for (const { lane, proof } of lanes) {
|
|
656
|
+
if (!isActiveChildLane(lane, nowMs)) {
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
const parentKey = childLaneParentKey(proof);
|
|
660
|
+
const bucket = activeByParent.get(parentKey);
|
|
661
|
+
activeByParent.set(parentKey, {
|
|
662
|
+
count: (bucket?.count ?? 0) + 1,
|
|
663
|
+
proof: bucket?.proof ?? proof
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
const buckets = [...activeByParent.values()];
|
|
667
|
+
if (buckets.length === 0) {
|
|
668
|
+
return metric(0, 'ratio', 'healthy', activeProofs[0]?.path ?? proofs[0]?.path ?? null, 'child_lanes', null);
|
|
669
|
+
}
|
|
670
|
+
const busiest = buckets.reduce((winner, bucket) => bucket.count > winner.count ? bucket : winner);
|
|
671
|
+
const pressure = thresholds.childLaneCap > 0 ? busiest.count / thresholds.childLaneCap : busiest.count;
|
|
672
|
+
if (pressure >= 1) {
|
|
673
|
+
findings.push({
|
|
674
|
+
code: 'child_lane_cap_pressure',
|
|
675
|
+
verdict: 'degraded',
|
|
676
|
+
message: `Child-lane cap pressure is ${busiest.count}/${thresholds.childLaneCap} for one parent run.`,
|
|
677
|
+
source_path: busiest.proof.path,
|
|
678
|
+
source_field: 'child_lanes'
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
return metric(pressure, 'ratio', pressure >= 1 ? 'degraded' : 'healthy', busiest.proof.path, 'child_lanes', `${busiest.count}/${thresholds.childLaneCap} active, pending, or unaccepted child lanes for one parent run`);
|
|
682
|
+
}
|
|
683
|
+
function evaluateLinearBudget(linearBudget, thresholds, findings) {
|
|
684
|
+
const budget = asRecord(linearBudget?.value);
|
|
685
|
+
if (!budget) {
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
const suppression = normalizeOptionalString(budget.suppression);
|
|
689
|
+
if (budget.cooldown_active === true || (suppression !== null && suppression !== 'none')) {
|
|
690
|
+
findings.push({
|
|
691
|
+
code: 'linear_budget_suppressed',
|
|
692
|
+
verdict: 'degraded',
|
|
693
|
+
message: 'Linear shared-budget state reports active cooldown or suppression.',
|
|
694
|
+
source_path: linearBudget?.path ?? null,
|
|
695
|
+
source_field: budget.cooldown_active === true ? 'cooldown_active' : 'suppression'
|
|
696
|
+
});
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
const lowBucket = ['requests', 'endpoint_requests', 'complexity', 'endpoint_complexity']
|
|
700
|
+
.map((field) => ({ field, bucket: asRecord(budget[field]) }))
|
|
701
|
+
.find(({ bucket }) => {
|
|
702
|
+
const limit = finiteNumber(bucket?.limit);
|
|
703
|
+
const remaining = finiteNumber(bucket?.remaining);
|
|
704
|
+
if (limit === null || limit <= 0 || remaining === null) {
|
|
705
|
+
return false;
|
|
706
|
+
}
|
|
707
|
+
return remaining / limit <= thresholds.linearHeadroomLowRatio;
|
|
708
|
+
});
|
|
709
|
+
if (lowBucket) {
|
|
710
|
+
findings.push({
|
|
711
|
+
code: 'linear_headroom_low',
|
|
712
|
+
verdict: 'degraded',
|
|
713
|
+
message: 'Linear shared-budget headroom is low.',
|
|
714
|
+
source_path: linearBudget?.path ?? null,
|
|
715
|
+
source_field: `${lowBucket.field}.remaining`
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
function firstRefreshTimestampSource(input) {
|
|
720
|
+
if (input.pollingHealth) {
|
|
721
|
+
return { path: input.pollingHealth.path, field: 'last_success_at' };
|
|
722
|
+
}
|
|
723
|
+
if (input.intakeState) {
|
|
724
|
+
return { path: input.intakeState.path, field: 'polling.last_success_at' };
|
|
725
|
+
}
|
|
726
|
+
if (input.statusDataset) {
|
|
727
|
+
return { path: input.statusDataset.path, field: 'polling.last_success_at' };
|
|
728
|
+
}
|
|
729
|
+
return null;
|
|
730
|
+
}
|
|
731
|
+
function looksLikeLinearBudget(value) {
|
|
732
|
+
const budget = asRecord(value);
|
|
733
|
+
if (!budget) {
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
if (budget.cooldown_active !== undefined ||
|
|
737
|
+
budget.suppression !== undefined ||
|
|
738
|
+
budget.retry_after_seconds !== undefined ||
|
|
739
|
+
budget.retryAfterSeconds !== undefined) {
|
|
740
|
+
return true;
|
|
741
|
+
}
|
|
742
|
+
return ['requests', 'endpoint_requests', 'complexity', 'endpoint_complexity']
|
|
743
|
+
.some((field) => asRecord(budget[field]) !== null);
|
|
744
|
+
}
|
|
745
|
+
function evaluateWorkerAuditHealth(workerAudits, findings) {
|
|
746
|
+
for (const audit of workerAudits) {
|
|
747
|
+
if (audit.lineCount > 0 && audit.summary.attempted_count === 0 && audit.malformedLineCount === 0) {
|
|
748
|
+
findings.push({
|
|
749
|
+
code: 'worker_audit_jsonl_unrecognized',
|
|
750
|
+
verdict: 'unknown',
|
|
751
|
+
message: 'Worker audit JSONL has entries, but none match the provider Linear audit contract.',
|
|
752
|
+
source_path: audit.path,
|
|
753
|
+
source_field: null
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
if (audit.summary.failure_count > 0 && audit.summary.success_count === 0) {
|
|
757
|
+
findings.push({
|
|
758
|
+
code: 'worker_audit_all_failed',
|
|
759
|
+
verdict: 'degraded',
|
|
760
|
+
message: 'Worker audit JSONL records only failed Linear helper operations.',
|
|
761
|
+
source_path: audit.path,
|
|
762
|
+
source_field: 'ok'
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
if (audit.summary.parallelization_entries.some((entry) => entry.ok) &&
|
|
766
|
+
readProviderLinearParallelizationSnapshots(audit.summary).length === 0) {
|
|
767
|
+
findings.push({
|
|
768
|
+
code: 'worker_audit_parallelization_unusable',
|
|
769
|
+
verdict: 'degraded',
|
|
770
|
+
message: 'Worker audit JSONL has successful parallelization entries that cannot be normalized.',
|
|
771
|
+
source_path: audit.path,
|
|
772
|
+
source_field: 'parallelization'
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
function selectPollingHealthArtifact(input) {
|
|
778
|
+
return (selectLatestJsonArtifact(input.explicit) ??
|
|
779
|
+
nestedJsonArtifact(input.intakeState, 'polling') ??
|
|
780
|
+
nestedJsonArtifact(input.statusDataset, 'polling'));
|
|
781
|
+
}
|
|
782
|
+
function selectLinearBudgetArtifact(input) {
|
|
783
|
+
const proofBudgets = input.proofs.flatMap((proof) => {
|
|
784
|
+
const artifact = nestedJsonArtifact({ path: proof.path, value: proof.value }, 'linear_budget');
|
|
785
|
+
return artifact ? [artifact] : [];
|
|
786
|
+
});
|
|
787
|
+
return (selectLatestJsonArtifact(input.explicit) ??
|
|
788
|
+
nestedJsonArtifact(input.pollingHealth, 'linear_budget') ??
|
|
789
|
+
selectStatusDatasetLinearBudgetArtifact(input.statusDataset) ??
|
|
790
|
+
selectLatestJsonArtifact(proofBudgets));
|
|
791
|
+
}
|
|
792
|
+
function selectStatusDatasetLinearBudgetArtifact(statusDataset) {
|
|
793
|
+
const combinedLinearBudget = nestedJsonArtifact(statusDataset, 'rate_limits.linear_budget');
|
|
794
|
+
if (combinedLinearBudget) {
|
|
795
|
+
return combinedLinearBudget;
|
|
796
|
+
}
|
|
797
|
+
const pollingLinearBudget = nestedJsonArtifact(statusDataset, 'polling.linear_budget');
|
|
798
|
+
if (pollingLinearBudget) {
|
|
799
|
+
return pollingLinearBudget;
|
|
800
|
+
}
|
|
801
|
+
const rateLimits = nestedJsonArtifact(statusDataset, 'rate_limits');
|
|
802
|
+
if (looksLikeLinearBudget(rateLimits?.value)) {
|
|
803
|
+
return rateLimits;
|
|
804
|
+
}
|
|
805
|
+
return null;
|
|
806
|
+
}
|
|
807
|
+
function findManifestForProof(proof, manifests) {
|
|
808
|
+
const proofRunId = normalizeOptionalString(proof.value.run_id);
|
|
809
|
+
if (proofRunId) {
|
|
810
|
+
const runMatches = manifests.filter((manifest) => normalizeOptionalString(manifest.value.run_id) === proofRunId);
|
|
811
|
+
const directoryMatches = runMatches.filter((manifest) => manifest.runDir === proof.runDir);
|
|
812
|
+
return selectLatestManifestArtifact(directoryMatches) ?? selectLatestManifestArtifact(runMatches);
|
|
813
|
+
}
|
|
814
|
+
return selectLatestManifestArtifact(manifests.filter((manifest) => manifest.runDir === proof.runDir));
|
|
815
|
+
}
|
|
816
|
+
function findLatestManifestForRunId(manifests, runId) {
|
|
817
|
+
return selectLatestManifestArtifact(manifests.filter((manifest) => normalizeOptionalString(manifest.value.run_id) === runId));
|
|
818
|
+
}
|
|
819
|
+
function resolveProofRunId(proof) {
|
|
820
|
+
return normalizeOptionalString(proof.manifest?.value.run_id) ?? normalizeOptionalString(proof.value.run_id);
|
|
821
|
+
}
|
|
822
|
+
function collectClaims(intakeState) {
|
|
823
|
+
const value = asRecord(intakeState?.value);
|
|
824
|
+
return collectArrayRecords(value?.claims);
|
|
825
|
+
}
|
|
826
|
+
function isActiveClaim(claim) {
|
|
827
|
+
return ACTIVE_CLAIM_STATES.has(readState(claim));
|
|
828
|
+
}
|
|
829
|
+
function collectStatusRetrying(statusDataset) {
|
|
830
|
+
const value = asRecord(statusDataset?.value);
|
|
831
|
+
return collectArrayRecords(value?.retrying);
|
|
832
|
+
}
|
|
833
|
+
function isActiveProof(proof) {
|
|
834
|
+
if (isTerminalProof(proof)) {
|
|
835
|
+
return false;
|
|
836
|
+
}
|
|
837
|
+
const manifestStatus = normalizeOptionalString(proof.manifest?.value.status)?.toLowerCase() ?? null;
|
|
838
|
+
if (manifestStatus && TERMINAL_MANIFEST_STATUSES.has(manifestStatus)) {
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
841
|
+
return true;
|
|
842
|
+
}
|
|
843
|
+
function isTerminalProof(proof) {
|
|
844
|
+
const manifestStatus = normalizeOptionalString(proof.manifest?.value.status)?.toLowerCase() ?? null;
|
|
845
|
+
if (manifestStatus && TERMINAL_MANIFEST_STATUSES.has(manifestStatus)) {
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
const ownerPhase = normalizeOptionalString(proof.value.owner_phase);
|
|
849
|
+
const ownerStatus = normalizeOptionalString(proof.value.owner_status);
|
|
850
|
+
return ownerPhase === 'ended' && (ownerStatus === 'succeeded' || ownerStatus === 'failed');
|
|
851
|
+
}
|
|
852
|
+
function firstHeartbeatTimestampMs(proof) {
|
|
853
|
+
return earliestTimestampMs(timestampMs(proof.value.first_heartbeat_at), timestampMs(proof.value.firstHeartbeatAt), timestampMs(proof.value.first_activity_at), timestampMs(proof.value.firstActivityAt), timestampMs(proof.value.first_event_at), timestampMs(proof.value.firstEventAt), timestampMs(proof.manifest?.value.first_heartbeat_at), timestampMs(proof.manifest?.value.firstHeartbeatAt), timestampMs(proof.manifest?.value.first_activity_at), timestampMs(proof.manifest?.value.firstActivityAt));
|
|
854
|
+
}
|
|
855
|
+
function isActiveChildLane(lane, nowMs) {
|
|
856
|
+
if (lane.in_flight_action) {
|
|
857
|
+
const startedAt = timestampMs(lane.in_flight_started_at);
|
|
858
|
+
return startedAt !== null && nowMs - startedAt < PROVIDER_LINEAR_CHILD_LANE_IN_FLIGHT_STALE_MS;
|
|
859
|
+
}
|
|
860
|
+
const decision = normalizeOptionalString(lane.decision);
|
|
861
|
+
return decision === 'pending';
|
|
862
|
+
}
|
|
863
|
+
function childLaneParentKey(proof) {
|
|
864
|
+
return (normalizeOptionalString(proof.value.parent_run_id) ??
|
|
865
|
+
normalizeOptionalString(proof.value.parentRunId) ??
|
|
866
|
+
normalizeOptionalString(proof.value.run_id) ??
|
|
867
|
+
normalizeOptionalString(proof.manifest?.value.parent_run_id) ??
|
|
868
|
+
normalizeOptionalString(proof.manifest?.value.run_id) ??
|
|
869
|
+
proof.runDir);
|
|
870
|
+
}
|
|
871
|
+
function readState(record) {
|
|
872
|
+
return normalizeOptionalString(record.state)?.toLowerCase() ?? 'unknown';
|
|
873
|
+
}
|
|
874
|
+
function selectLatestJsonArtifact(artifacts) {
|
|
875
|
+
if (artifacts.length === 0) {
|
|
876
|
+
return null;
|
|
877
|
+
}
|
|
878
|
+
return [...artifacts].sort(compareJsonArtifactsByFreshness).at(-1) ?? null;
|
|
879
|
+
}
|
|
880
|
+
function selectLatestManifestArtifact(manifests) {
|
|
881
|
+
return [...manifests].sort(compareJsonArtifactsByFreshness).at(-1) ?? null;
|
|
882
|
+
}
|
|
883
|
+
function compareJsonArtifactsByFreshness(left, right) {
|
|
884
|
+
const leftTs = artifactTimestampMs(left) ?? Number.NEGATIVE_INFINITY;
|
|
885
|
+
const rightTs = artifactTimestampMs(right) ?? Number.NEGATIVE_INFINITY;
|
|
886
|
+
if (leftTs !== rightTs) {
|
|
887
|
+
return leftTs - rightTs;
|
|
888
|
+
}
|
|
889
|
+
return left.path.localeCompare(right.path);
|
|
890
|
+
}
|
|
891
|
+
function artifactTimestampMs(artifact) {
|
|
892
|
+
const value = asRecord(artifact.value);
|
|
893
|
+
return latestTimestampMs(timestampMs(value?.updated_at), timestampMs(value?.generated_at), timestampMs(value?.observed_at), timestampMs(value?.recorded_at), timestampMs(value?.last_success_at), timestampMs(value?.lastSuccessAt), timestampMs(value?.last_completed_at), timestampMs(value?.lastCompletedAt));
|
|
894
|
+
}
|
|
895
|
+
function nestedJsonArtifact(artifact, fieldPath) {
|
|
896
|
+
if (!artifact) {
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
const value = readPath(artifact.value, fieldPath);
|
|
900
|
+
return value && typeof value === 'object' ? { path: artifact.path, value } : null;
|
|
901
|
+
}
|
|
902
|
+
function timestampCandidate(artifact, fieldPath) {
|
|
903
|
+
const timestamp = timestampMs(readPath(artifact?.value, fieldPath));
|
|
904
|
+
return timestamp === null ? null : { timestampMs: timestamp, path: artifact?.path ?? null, field: fieldPath };
|
|
905
|
+
}
|
|
906
|
+
function selectLatestTimestampCandidate(candidates) {
|
|
907
|
+
return candidates.length === 0
|
|
908
|
+
? null
|
|
909
|
+
: candidates.reduce((winner, candidate) => candidate.timestampMs > winner.timestampMs ? candidate : winner);
|
|
910
|
+
}
|
|
911
|
+
function metric(value, unit, verdict, sourcePath, sourceField, reason) {
|
|
912
|
+
return {
|
|
913
|
+
value,
|
|
914
|
+
unit,
|
|
915
|
+
verdict,
|
|
916
|
+
source_path: sourcePath,
|
|
917
|
+
source_field: sourceField,
|
|
918
|
+
reason
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
function buildUnknownVerdictMetric() {
|
|
922
|
+
return metric(null, null, 'unknown', null, null, 'verdict not evaluated yet');
|
|
923
|
+
}
|
|
924
|
+
function resolveOverallVerdict(findings) {
|
|
925
|
+
if (findings.some((finding) => finding.verdict === 'contradictory')) {
|
|
926
|
+
return 'contradictory';
|
|
927
|
+
}
|
|
928
|
+
if (findings.some((finding) => finding.verdict === 'stale')) {
|
|
929
|
+
return 'stale';
|
|
930
|
+
}
|
|
931
|
+
if (findings.some((finding) => finding.verdict === 'degraded')) {
|
|
932
|
+
return 'degraded';
|
|
933
|
+
}
|
|
934
|
+
if (findings.some((finding) => finding.verdict === 'unknown')) {
|
|
935
|
+
return 'unknown';
|
|
936
|
+
}
|
|
937
|
+
return 'healthy';
|
|
938
|
+
}
|
|
939
|
+
function firstStaleSourcePath(findings) {
|
|
940
|
+
return findings.find((finding) => finding.verdict === 'contradictory' || finding.verdict === 'stale')?.source_path ?? null;
|
|
941
|
+
}
|
|
942
|
+
function normalizeThresholds(input) {
|
|
943
|
+
return {
|
|
944
|
+
...DEFAULT_THRESHOLDS,
|
|
945
|
+
...Object.fromEntries(Object.entries(input ?? {}).flatMap(([key, value]) => typeof value === 'number' && Number.isFinite(value) && value >= 0 ? [[key, value]] : []))
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
function normalizePathList(paths) {
|
|
949
|
+
return Array.isArray(paths) ? paths.map((path) => resolve(path)) : [];
|
|
950
|
+
}
|
|
951
|
+
function appendUnique(target, values) {
|
|
952
|
+
const seen = new Set(target);
|
|
953
|
+
for (const value of values) {
|
|
954
|
+
if (!seen.has(value)) {
|
|
955
|
+
target.push(value);
|
|
956
|
+
seen.add(value);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
function normalizeNowMs(value) {
|
|
961
|
+
if (value instanceof Date) {
|
|
962
|
+
const timestamp = value.getTime();
|
|
963
|
+
if (!Number.isFinite(timestamp)) {
|
|
964
|
+
throw new Error(`Invalid now value: ${String(value)}.`);
|
|
965
|
+
}
|
|
966
|
+
return timestamp;
|
|
967
|
+
}
|
|
968
|
+
if (typeof value === 'number') {
|
|
969
|
+
if (!Number.isFinite(value)) {
|
|
970
|
+
throw new Error(`Invalid now value: ${String(value)}.`);
|
|
971
|
+
}
|
|
972
|
+
return value;
|
|
973
|
+
}
|
|
974
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
975
|
+
const parsed = Date.parse(value);
|
|
976
|
+
if (!Number.isFinite(parsed)) {
|
|
977
|
+
throw new Error(`Invalid now value: ${value}.`);
|
|
978
|
+
}
|
|
979
|
+
return parsed;
|
|
980
|
+
}
|
|
981
|
+
return Date.now();
|
|
982
|
+
}
|
|
983
|
+
function timestampMs(value) {
|
|
984
|
+
const string = normalizeOptionalString(value);
|
|
985
|
+
if (!string) {
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
const parsed = Date.parse(string);
|
|
989
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
990
|
+
}
|
|
991
|
+
function firstTimestampMs(record, field) {
|
|
992
|
+
return timestampMs(asRecord(record)?.[field]);
|
|
993
|
+
}
|
|
994
|
+
function latestTimestampMs(...values) {
|
|
995
|
+
const finite = values.filter((value) => typeof value === 'number' && Number.isFinite(value));
|
|
996
|
+
return finite.length === 0 ? null : Math.max(...finite);
|
|
997
|
+
}
|
|
998
|
+
function earliestTimestampMs(...values) {
|
|
999
|
+
const finite = values.filter((value) => typeof value === 'number' && Number.isFinite(value));
|
|
1000
|
+
return finite.length === 0 ? null : Math.min(...finite);
|
|
1001
|
+
}
|
|
1002
|
+
function finiteNumber(value) {
|
|
1003
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
1004
|
+
}
|
|
1005
|
+
function normalizeOptionalString(value) {
|
|
1006
|
+
if (typeof value !== 'string') {
|
|
1007
|
+
return null;
|
|
1008
|
+
}
|
|
1009
|
+
const trimmed = value.trim();
|
|
1010
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1011
|
+
}
|
|
1012
|
+
function asRecord(value) {
|
|
1013
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
1014
|
+
? value
|
|
1015
|
+
: null;
|
|
1016
|
+
}
|
|
1017
|
+
function collectArrayRecords(value) {
|
|
1018
|
+
return Array.isArray(value)
|
|
1019
|
+
? value.filter((entry) => asRecord(entry) !== null)
|
|
1020
|
+
: [];
|
|
1021
|
+
}
|
|
1022
|
+
function hasCoreFreshnessSources(sources) {
|
|
1023
|
+
return [
|
|
1024
|
+
sources.provider_intake_state,
|
|
1025
|
+
sources.provider_proofs,
|
|
1026
|
+
sources.status_datasets,
|
|
1027
|
+
sources.polling_health
|
|
1028
|
+
].some((entries) => entries.length > 0);
|
|
1029
|
+
}
|
|
1030
|
+
function firstAuxiliarySourcePath(sources) {
|
|
1031
|
+
return sources.provider_manifests[0] ?? sources.worker_audit_jsonl[0] ?? sources.linear_budget_state[0] ?? null;
|
|
1032
|
+
}
|
|
1033
|
+
function readPath(value, fieldPath) {
|
|
1034
|
+
return fieldPath.split('.').reduce((current, key) => asRecord(current)?.[key], value);
|
|
1035
|
+
}
|
|
1036
|
+
function describeError(error) {
|
|
1037
|
+
return error instanceof Error ? error.message : String(error);
|
|
1038
|
+
}
|
|
1039
|
+
export function formatProviderControlHostFreshnessGaugeText(report) {
|
|
1040
|
+
const root = report.sources.artifact_root ?? 'explicit paths';
|
|
1041
|
+
const lines = [
|
|
1042
|
+
`Provider/control-host freshness gauge: ${report.verdict}`,
|
|
1043
|
+
`Generated: ${report.generated_at}`,
|
|
1044
|
+
`Artifact root: ${root}`
|
|
1045
|
+
];
|
|
1046
|
+
for (const finding of report.findings) {
|
|
1047
|
+
const path = finding.source_path ? ` (${relativeOrOriginal(process.cwd(), finding.source_path)})` : '';
|
|
1048
|
+
lines.push(`- ${finding.verdict}: ${finding.code}${path} - ${finding.message}`);
|
|
1049
|
+
}
|
|
1050
|
+
return lines.join('\n');
|
|
1051
|
+
}
|
|
1052
|
+
function relativeOrOriginal(from, path) {
|
|
1053
|
+
const relativePath = relative(from, path);
|
|
1054
|
+
return relativePath && !relativePath.startsWith('..') ? relativePath : path;
|
|
1055
|
+
}
|
|
1056
|
+
export async function assertProviderControlHostFreshnessArtifactRoot(path) {
|
|
1057
|
+
const resolvedPath = resolve(path);
|
|
1058
|
+
let stats;
|
|
1059
|
+
try {
|
|
1060
|
+
stats = await stat(resolvedPath);
|
|
1061
|
+
}
|
|
1062
|
+
catch {
|
|
1063
|
+
throw new Error(`Artifact root does not exist: ${path}`);
|
|
1064
|
+
}
|
|
1065
|
+
if (!stats.isDirectory()) {
|
|
1066
|
+
throw new Error(`Artifact root must be a directory: ${path}`);
|
|
1067
|
+
}
|
|
1068
|
+
}
|