@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,1885 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
3
|
+
import { stripNonApplicableGuardrailSummaryLines } from '../run/manifest.js';
|
|
4
|
+
import { LINEAR_ADVISORY_STATE_FILE } from './controlPersistenceFiles.js';
|
|
5
|
+
import { resolveLegacyWorkspacePathFromRunDir, resolveManifestWorkspacePath, resolveProviderWorkspacePath } from '../run/workspacePath.js';
|
|
6
|
+
import { PROVIDER_LINEAR_CHILD_LANE_PIPELINE_ID, PROVIDER_LINEAR_CHILD_LANE_RESERVED_SUMMARY, PROVIDER_LINEAR_WORKER_CHILD_LANES_FILENAME, PROVIDER_LINEAR_WORKER_PROOF_FILENAME, refreshProviderLinearWorkerProofSnapshot } from '../providerLinearWorkerRunner.js';
|
|
7
|
+
import { buildTrackedLinearPayload } from './observabilityReadModel.js';
|
|
8
|
+
import { buildProviderFallbackTaskId, buildProviderIssueKey, hasQueuedProviderIntakeRetry, readProviderIntakeClaim, selectProviderIntakeClaim } from './providerIntakeState.js';
|
|
9
|
+
import { classifyProviderLinearWorkflowState } from './providerLinearWorkflowStates.js';
|
|
10
|
+
import { buildProviderIssueDebugSnapshot } from './providerIssueObservability.js';
|
|
11
|
+
import { writeJsonAtomic } from '../utils/fs.js';
|
|
12
|
+
import { buildProviderLinearWorkerTerminalSummary, deriveDeterministicProviderMutationSuppressions, formatDeterministicProviderMutationDegradationSummary, isAuxiliaryProviderProofHarnessManifest, isProviderLinearWorkerProofFreshForStage, resolveProviderLinearWorkerAttemptStartedAt, resolveProviderLinearWorkerTerminalReason, resolveProviderLinearWorkerTerminalStatus, shouldUseProviderLinearWorkerTerminalProofForSelectedRun } from './providerLinearWorkerTruth.js';
|
|
13
|
+
const PROVIDER_LINEAR_WORKER_PIPELINE_TITLE = 'Provider Linear Worker';
|
|
14
|
+
const PROVIDER_LINEAR_WORKER_PIPELINE_ID = 'provider-linear-worker';
|
|
15
|
+
const PROVIDER_LINEAR_WORKER_RECONCILIATION_FILENAME = 'provider-linear-worker-reconciliation.json';
|
|
16
|
+
const SYNTHETIC_LINEAR_TASK_ID_PATTERN = /^linear-[a-z0-9]+(?:-[a-z0-9]+)*$/i;
|
|
17
|
+
const PROVIDER_LINEAR_CHILD_PIPELINE_IDS = new Set([
|
|
18
|
+
'docs-review',
|
|
19
|
+
'implementation-gate',
|
|
20
|
+
'docs-relevance-advisory',
|
|
21
|
+
'provider-linear-child-lane'
|
|
22
|
+
]);
|
|
23
|
+
function selectFreshLinearAdvisoryTrackedIssue(advisoryState) {
|
|
24
|
+
if (!advisoryState || advisoryState.stale_source) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return advisoryState.tracked_issue ?? null;
|
|
28
|
+
}
|
|
29
|
+
export function createSelectedRunProjectionReader(context) {
|
|
30
|
+
let selectedSnapshotPromise = null;
|
|
31
|
+
let selectedContextPromise = null;
|
|
32
|
+
let compatibilityContextPromise = null;
|
|
33
|
+
const readSelectedRunManifestSnapshot = async () => {
|
|
34
|
+
selectedSnapshotPromise ??= readSelectedRunManifestSnapshotInternal(context);
|
|
35
|
+
return selectedSnapshotPromise;
|
|
36
|
+
};
|
|
37
|
+
const buildSelectedRunContext = async (snapshot = null) => {
|
|
38
|
+
if (snapshot) {
|
|
39
|
+
return buildSelectedRunContextFromSnapshot(context, snapshot);
|
|
40
|
+
}
|
|
41
|
+
selectedContextPromise ??= (async () => {
|
|
42
|
+
const selectedSnapshot = await readSelectedRunManifestSnapshot();
|
|
43
|
+
return buildSelectedRunContextFromSnapshot(context, selectedSnapshot);
|
|
44
|
+
})();
|
|
45
|
+
return selectedContextPromise;
|
|
46
|
+
};
|
|
47
|
+
const buildCompatibilitySourceContext = async (snapshot = null) => {
|
|
48
|
+
if (snapshot) {
|
|
49
|
+
return buildCompatibilitySourceContextFromSnapshot(context, snapshot);
|
|
50
|
+
}
|
|
51
|
+
compatibilityContextPromise ??= (async () => {
|
|
52
|
+
const selectedSnapshot = await readSelectedRunManifestSnapshot();
|
|
53
|
+
return buildCompatibilitySourceContextFromSnapshot(context, selectedSnapshot);
|
|
54
|
+
})();
|
|
55
|
+
return compatibilityContextPromise;
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
readSelectedRunManifestSnapshot,
|
|
59
|
+
buildSelectedRunContext,
|
|
60
|
+
buildCompatibilitySourceContext
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export async function discoverCompatibilityCollectionContexts(context) {
|
|
64
|
+
const runsRoot = resolveRunsRootFromRunDir(context.paths.runDir);
|
|
65
|
+
const currentTaskId = resolveTaskIdFromManifestPath(context.paths.manifestPath);
|
|
66
|
+
const currentRunId = resolveRunIdFromManifestPath(context.paths.manifestPath);
|
|
67
|
+
if (!runsRoot) {
|
|
68
|
+
return { running: [], retrying: [], all: [] };
|
|
69
|
+
}
|
|
70
|
+
const controlWorkspacePath = await resolveControlWorkspacePath(context);
|
|
71
|
+
const selectedSnapshot = await readSelectedRunManifestSnapshotInternal(context);
|
|
72
|
+
const selectedContext = await buildUnreconciledCompatibilitySourceContextFromSnapshot(context, selectedSnapshot);
|
|
73
|
+
const discovered = await readDiscoveredTaskCompatibilityContexts(context, runsRoot, currentTaskId, currentRunId, controlWorkspacePath);
|
|
74
|
+
const reconciled = await reconcileProviderLinearWorkerDiscoveredContexts(discovered, context.providerIntakeState ?? null, selectedContext ? [selectedContext] : []);
|
|
75
|
+
const running = [];
|
|
76
|
+
const retrying = [];
|
|
77
|
+
const all = [];
|
|
78
|
+
for (const entry of reconciled) {
|
|
79
|
+
const discoveredContext = entry.context;
|
|
80
|
+
all.push(discoveredContext);
|
|
81
|
+
if (discoveredContext.rawStatus === 'in_progress') {
|
|
82
|
+
running.push(discoveredContext);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if ((context.providerIntakeState?.claims.length ?? 0) === 0 && entry.retryFallbackEligible) {
|
|
86
|
+
retrying.push(discoveredContext);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return { running, retrying, all };
|
|
90
|
+
}
|
|
91
|
+
async function readDiscoveredTaskCompatibilityContexts(context, runsRoot, currentTaskId, currentRunId, controlWorkspacePath) {
|
|
92
|
+
const discovered = [];
|
|
93
|
+
const taskEntries = await readDirectoryNames(runsRoot);
|
|
94
|
+
for (const taskEntry of taskEntries.sort((left, right) => left.localeCompare(right)).reverse()) {
|
|
95
|
+
if (taskEntry === 'local-mcp') {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const discoveredContexts = await readTaskCompatibilityContexts(join(runsRoot, taskEntry, 'cli'), {
|
|
99
|
+
excludeRunId: taskEntry === currentTaskId ? currentRunId : null,
|
|
100
|
+
providerIntakeState: context.providerIntakeState,
|
|
101
|
+
controlWorkspacePath
|
|
102
|
+
});
|
|
103
|
+
discovered.push(...discoveredContexts);
|
|
104
|
+
}
|
|
105
|
+
return discovered;
|
|
106
|
+
}
|
|
107
|
+
export async function discoverAuthoritativeRetryCollectionContexts(context) {
|
|
108
|
+
if (!context.providerIntakeState) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
const retrying = [];
|
|
112
|
+
for (const claim of context.providerIntakeState.claims) {
|
|
113
|
+
if (!hasQueuedProviderIntakeRetry(claim)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
retrying.push(await buildProviderRetryContextFromClaim(context, claim));
|
|
117
|
+
}
|
|
118
|
+
return retrying.sort((left, right) => Date.parse(right.updatedAt ?? '') - Date.parse(left.updatedAt ?? ''));
|
|
119
|
+
}
|
|
120
|
+
async function buildSelectedRunContextFromSnapshot(context, snapshot) {
|
|
121
|
+
return await reconcileSelectedProviderLinearWorkerContext(context, await buildUnreconciledCompatibilitySourceContextFromSnapshot(context, snapshot));
|
|
122
|
+
}
|
|
123
|
+
async function buildCompatibilitySourceContextFromSnapshot(context, snapshot) {
|
|
124
|
+
return await reconcileSelectedProviderLinearWorkerContext(context, await buildUnreconciledCompatibilitySourceContextFromSnapshot(context, snapshot));
|
|
125
|
+
}
|
|
126
|
+
async function buildUnreconciledCompatibilitySourceContextFromSnapshot(context, snapshot) {
|
|
127
|
+
const [parts, controlWorkspacePath] = await Promise.all([
|
|
128
|
+
resolveProjectionContextParts(context, snapshot),
|
|
129
|
+
resolveControlWorkspacePath(context)
|
|
130
|
+
]);
|
|
131
|
+
const providerClaim = findMatchingProviderIntakeClaim(context.providerIntakeState, snapshot, parts.providerLinearWorkerProof);
|
|
132
|
+
return buildProjectionContextFromParts(snapshot, parts, resolveRunsRootFromRunDir(context.paths.runDir), controlWorkspacePath, providerClaim, context.providerIntakeState ?? null);
|
|
133
|
+
}
|
|
134
|
+
async function reconcileSelectedProviderLinearWorkerContext(context, selected) {
|
|
135
|
+
if (!selected || !context.providerIntakeState) {
|
|
136
|
+
return selected;
|
|
137
|
+
}
|
|
138
|
+
if (!isProviderLinearWorkerReconciliationSource(selected) ||
|
|
139
|
+
!isActiveLookingProviderLinearWorkerManifestStatus(selected.rawStatus)) {
|
|
140
|
+
return selected;
|
|
141
|
+
}
|
|
142
|
+
const selectedRunDir = selected.runDir ?? (selected.manifestPath ? dirname(selected.manifestPath) : context.paths.runDir);
|
|
143
|
+
const selectedManifestPath = selected.manifestPath ?? context.paths.manifestPath;
|
|
144
|
+
const runsRoot = resolveRunsRootFromRunDir(selectedRunDir);
|
|
145
|
+
const currentTaskId = resolveTaskIdFromManifestPath(selectedManifestPath) ?? selected.taskId;
|
|
146
|
+
const currentRunId = resolveRunIdFromManifestPath(selectedManifestPath) ?? selected.runId;
|
|
147
|
+
if (!runsRoot) {
|
|
148
|
+
return selected;
|
|
149
|
+
}
|
|
150
|
+
const controlWorkspacePath = await resolveControlWorkspacePath(context);
|
|
151
|
+
const discovered = await readDiscoveredTaskCompatibilityContexts(context, runsRoot, currentTaskId, currentRunId, controlWorkspacePath);
|
|
152
|
+
const [reconciled] = await reconcileProviderLinearWorkerDiscoveredContexts([{ context: selected, retryFallbackEligible: false }], context.providerIntakeState, discovered.map((entry) => entry.context));
|
|
153
|
+
return (reconciled?.context ?? selected);
|
|
154
|
+
}
|
|
155
|
+
function buildProjectionContextFromParts(snapshot, parts, controlRunsRoot, controlWorkspacePath, providerClaim = null, providerIntakeState = null) {
|
|
156
|
+
if (!snapshot) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const { manifestRecord, taskId, runId } = snapshot;
|
|
160
|
+
const issueProvider = snapshot.issueProvider ?? providerClaim?.provider ?? null;
|
|
161
|
+
const allowTrackedIssueFallbackIdentityRebinding = hasProviderLinearClaimBindingProvenance(snapshot, parts.providerLinearWorkerProof);
|
|
162
|
+
const { issueIdentifier, issueId, lookupAliases } = resolveProjectionIssueIdentity(snapshot, parts.trackedIssue, providerClaim, allowTrackedIssueFallbackIdentityRebinding);
|
|
163
|
+
const matchedTrackedIssue = resolveProjectionTrackedIssue(parts.trackedIssue, {
|
|
164
|
+
issueIdentifier,
|
|
165
|
+
issueId,
|
|
166
|
+
taskId,
|
|
167
|
+
runId
|
|
168
|
+
});
|
|
169
|
+
const control = parts.control;
|
|
170
|
+
const manifestRawStatus = readStringValue(manifestRecord, 'status') ?? 'unknown';
|
|
171
|
+
const startedAt = readStringValue(manifestRecord, 'started_at', 'startedAt') ?? null;
|
|
172
|
+
const providerProofRecord = (parts.providerLinearWorkerProof ?? null);
|
|
173
|
+
const proofIsFreshForStage = isProviderLinearWorkerProofFreshForStage(providerProofRecord, startedAt);
|
|
174
|
+
const useTerminalProof = shouldUseProviderLinearWorkerTerminalProofForSelectedRun(manifestRecord, providerProofRecord);
|
|
175
|
+
const useScopedTerminalProof = useTerminalProof && proofIsFreshForStage;
|
|
176
|
+
const proofTerminalStatus = useScopedTerminalProof
|
|
177
|
+
? resolveProviderLinearWorkerTerminalStatus(providerProofRecord)
|
|
178
|
+
: null;
|
|
179
|
+
const rawStatus = proofTerminalStatus ?? manifestRawStatus;
|
|
180
|
+
const manifestUpdatedAt = readStringValue(manifestRecord, 'updated_at', 'updatedAt') ?? null;
|
|
181
|
+
const proofUpdatedAt = useScopedTerminalProof
|
|
182
|
+
? readStringValue(providerProofRecord ?? {}, 'updated_at')
|
|
183
|
+
: null;
|
|
184
|
+
const updatedAt = proofUpdatedAt && (!manifestUpdatedAt || compareIsoTimestamp(proofUpdatedAt, manifestUpdatedAt) >= 0)
|
|
185
|
+
? proofUpdatedAt
|
|
186
|
+
: manifestUpdatedAt;
|
|
187
|
+
const manifestCompletedAt = readStringValue(manifestRecord, 'completed_at', 'completedAt');
|
|
188
|
+
const completedAt = manifestCompletedAt ?? (isTerminalRunStatus(rawStatus) ? proofUpdatedAt ?? updatedAt : null);
|
|
189
|
+
const manifestSummary = stripNonApplicableGuardrailSummaryLines(manifestRecord, readStringValue(manifestRecord, 'summary'));
|
|
190
|
+
const proofAttemptStartedAt = useScopedTerminalProof
|
|
191
|
+
? resolveProviderLinearWorkerAttemptStartedAt(providerProofRecord)
|
|
192
|
+
: null;
|
|
193
|
+
const proofSummary = useScopedTerminalProof && proofTerminalStatus
|
|
194
|
+
? buildProviderLinearWorkerTerminalSummary({
|
|
195
|
+
status: proofTerminalStatus,
|
|
196
|
+
endReason: resolveProviderLinearWorkerTerminalReason(providerProofRecord),
|
|
197
|
+
degradationSummary: proofAttemptStartedAt === null
|
|
198
|
+
? null
|
|
199
|
+
: formatDeterministicProviderMutationDegradationSummary(deriveDeterministicProviderMutationSuppressions(parts.providerLinearWorkerProof?.linear_audit ?? null, {
|
|
200
|
+
recordedAtNotBefore: proofAttemptStartedAt,
|
|
201
|
+
issueId: parts.providerLinearWorkerProof?.issue_id ?? null
|
|
202
|
+
}))
|
|
203
|
+
})
|
|
204
|
+
: null;
|
|
205
|
+
const providerDebugSnapshot = buildProviderIssueDebugSnapshot({
|
|
206
|
+
tracked_issue: matchedTrackedIssue,
|
|
207
|
+
claim: providerClaim,
|
|
208
|
+
proof: proofIsFreshForStage ? parts.providerLinearWorkerProof : null,
|
|
209
|
+
rehydrated_at: providerIntakeState?.rehydrated_at ?? null
|
|
210
|
+
});
|
|
211
|
+
const terminalMergeCloseoutProgress = resolveTerminalMergeCloseoutProgress({
|
|
212
|
+
rawStatus,
|
|
213
|
+
providerDebugSnapshot,
|
|
214
|
+
trackedIssue: matchedTrackedIssue
|
|
215
|
+
});
|
|
216
|
+
const summary = resolveSelectedRunDisplaySummary({
|
|
217
|
+
manifestRecord,
|
|
218
|
+
rawStatus,
|
|
219
|
+
summary: proofSummary ?? manifestSummary,
|
|
220
|
+
terminalMergeCloseoutProgress
|
|
221
|
+
});
|
|
222
|
+
const workspacePath = resolveSelectedRunWorkspacePath({
|
|
223
|
+
manifestRecord,
|
|
224
|
+
manifestPath: snapshot.manifestPath,
|
|
225
|
+
controlRunsRoot,
|
|
226
|
+
controlWorkspacePath
|
|
227
|
+
});
|
|
228
|
+
const questionSummary = buildSelectedRunQuestionSummary(parts.questions);
|
|
229
|
+
const latestAction = control.latest_action?.action ?? null;
|
|
230
|
+
const compatibilityState = resolveCompatibilityState(shouldPreferTrackedIssueCompatibilityState(matchedTrackedIssue, providerClaim)
|
|
231
|
+
? matchedTrackedIssue
|
|
232
|
+
: null, providerClaim);
|
|
233
|
+
const { displayStatus, statusReason } = resolveSelectedRunDisplayStatus({
|
|
234
|
+
rawStatus,
|
|
235
|
+
latestAction,
|
|
236
|
+
questionSummary,
|
|
237
|
+
compatibilityState,
|
|
238
|
+
terminalMergeCloseoutProgress
|
|
239
|
+
});
|
|
240
|
+
const tracked = buildTrackedLinearPayload(matchedTrackedIssue);
|
|
241
|
+
const latestEvent = buildSelectedRunLatestEvent({
|
|
242
|
+
controlAction: control.latest_action ?? null,
|
|
243
|
+
updatedAt,
|
|
244
|
+
summary,
|
|
245
|
+
fallbackEvent: rawStatus,
|
|
246
|
+
providerDebugSnapshot,
|
|
247
|
+
terminalMergeCloseoutProgress
|
|
248
|
+
});
|
|
249
|
+
const lastError = terminalMergeCloseoutProgress?.status === 'failed'
|
|
250
|
+
? terminalMergeCloseoutProgress.stall_reason ??
|
|
251
|
+
summary ??
|
|
252
|
+
'merge_closeout_failed'
|
|
253
|
+
: rawStatus === 'failed'
|
|
254
|
+
? summary ?? control.latest_action?.reason ?? 'run_failed'
|
|
255
|
+
: latestAction === 'fail'
|
|
256
|
+
? control.latest_action?.reason ?? manifestSummary ?? 'run_failed'
|
|
257
|
+
: null;
|
|
258
|
+
return {
|
|
259
|
+
issueProvider,
|
|
260
|
+
issueIdentifier,
|
|
261
|
+
issueId,
|
|
262
|
+
taskId,
|
|
263
|
+
runId,
|
|
264
|
+
lookupAliases,
|
|
265
|
+
rawStatus,
|
|
266
|
+
displayStatus,
|
|
267
|
+
statusReason,
|
|
268
|
+
startedAt,
|
|
269
|
+
updatedAt,
|
|
270
|
+
completedAt,
|
|
271
|
+
summary,
|
|
272
|
+
lastError,
|
|
273
|
+
latestAction,
|
|
274
|
+
latestEvent,
|
|
275
|
+
workspacePath,
|
|
276
|
+
pipelineId: readStringValue(manifestRecord, 'pipeline_id', 'pipelineId') ?? null,
|
|
277
|
+
pipelineTitle: readStringValue(manifestRecord, 'pipeline_title', 'pipelineTitle') ?? null,
|
|
278
|
+
stages: readManifestStageSummaries(manifestRecord),
|
|
279
|
+
approvalsTotal: readManifestApprovalsTotal(manifestRecord),
|
|
280
|
+
manifestPath: snapshot.manifestPath,
|
|
281
|
+
runDir: snapshot.runDir,
|
|
282
|
+
questionSummary,
|
|
283
|
+
tracked,
|
|
284
|
+
compatibilityState: compatibilityState?.state ?? null,
|
|
285
|
+
providerLinearWorkerProof: parts.providerLinearWorkerProof,
|
|
286
|
+
providerDebugSnapshot,
|
|
287
|
+
providerRetryState: buildProviderRetryState(providerClaim)
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function resolveProjectionIssueIdentity(snapshot, trackedIssue, providerClaim, allowTrackedIssueFallbackIdentityRebinding) {
|
|
291
|
+
const manifestIssueIdentifier = isProjectionFallbackIdentityValue(snapshot.issueIdentifier, snapshot)
|
|
292
|
+
? null
|
|
293
|
+
: snapshot.issueIdentifier;
|
|
294
|
+
const manifestIssueId = isProjectionFallbackIdentityValue(snapshot.issueId, snapshot)
|
|
295
|
+
? null
|
|
296
|
+
: snapshot.issueId;
|
|
297
|
+
const issueIdentifier = manifestIssueIdentifier ??
|
|
298
|
+
providerClaim?.issue_identifier ??
|
|
299
|
+
(allowTrackedIssueFallbackIdentityRebinding ? trackedIssue?.identifier : null) ??
|
|
300
|
+
snapshot.issueIdentifier;
|
|
301
|
+
const trackedIssueId = allowTrackedIssueFallbackIdentityRebinding &&
|
|
302
|
+
trackedIssue?.identifier === issueIdentifier
|
|
303
|
+
? trackedIssue.id
|
|
304
|
+
: null;
|
|
305
|
+
const issueId = manifestIssueId ??
|
|
306
|
+
providerClaim?.issue_id ??
|
|
307
|
+
trackedIssueId ??
|
|
308
|
+
snapshot.issueId;
|
|
309
|
+
return {
|
|
310
|
+
issueIdentifier,
|
|
311
|
+
issueId,
|
|
312
|
+
lookupAliases: Array.from(new Set(snapshot.lookupAliases.concat(buildProjectionLookupAliases({
|
|
313
|
+
issueIdentifier,
|
|
314
|
+
issueId,
|
|
315
|
+
taskId: snapshot.taskId,
|
|
316
|
+
runId: snapshot.runId
|
|
317
|
+
}))))
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function resolveProjectionTrackedIssue(trackedIssue, identity) {
|
|
321
|
+
if (!trackedIssue) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
if (!hasAuthoritativeProjectionIssueIdentity(identity)) {
|
|
325
|
+
return trackedIssue;
|
|
326
|
+
}
|
|
327
|
+
if (identity.issueId && trackedIssue.id === identity.issueId) {
|
|
328
|
+
return trackedIssue;
|
|
329
|
+
}
|
|
330
|
+
if (trackedIssue.identifier === identity.issueIdentifier) {
|
|
331
|
+
return trackedIssue;
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
function isProjectionFallbackIdentityValue(value, input) {
|
|
336
|
+
if (!value) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
return (isProjectionFallbackIdentityAlias(value, input.taskId) ||
|
|
340
|
+
isProjectionFallbackIdentityAlias(value, input.runId));
|
|
341
|
+
}
|
|
342
|
+
function isProjectionFallbackIdentityAlias(value, candidate) {
|
|
343
|
+
if (!candidate) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
if (value === candidate) {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
return SYNTHETIC_LINEAR_TASK_ID_PATTERN.test(value) && candidate.startsWith(`${value}-`);
|
|
350
|
+
}
|
|
351
|
+
function resolveSelectedRunWorkspacePath(input) {
|
|
352
|
+
const explicitWorkspacePath = resolveManifestWorkspacePath(input.manifestRecord);
|
|
353
|
+
if (explicitWorkspacePath) {
|
|
354
|
+
return explicitWorkspacePath;
|
|
355
|
+
}
|
|
356
|
+
if (!input.controlRunsRoot ||
|
|
357
|
+
!isCliRunManifestPathWithinRunsRoot(input.manifestPath, input.controlRunsRoot)) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
return input.controlWorkspacePath;
|
|
361
|
+
}
|
|
362
|
+
function resolveCompatibilityState(trackedIssue, providerClaim) {
|
|
363
|
+
const state = trackedIssue?.state ?? providerClaim?.issue_state ?? null;
|
|
364
|
+
const stateType = trackedIssue?.state_type ?? providerClaim?.issue_state_type ?? null;
|
|
365
|
+
if (!state && !stateType) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
state,
|
|
370
|
+
stateType
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function shouldPreferTrackedIssueCompatibilityState(trackedIssue, providerClaim) {
|
|
374
|
+
if (!trackedIssue) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
if (providerClaim?.reason !== 'provider_issue_rehydrated_active_run') {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
const trackedUpdatedAt = trackedIssue.updated_at ?? null;
|
|
381
|
+
const claimUpdatedAt = providerClaim.issue_updated_at ?? null;
|
|
382
|
+
if (!claimUpdatedAt) {
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
if (!trackedUpdatedAt) {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
return compareIsoTimestamp(trackedUpdatedAt, claimUpdatedAt) > 0;
|
|
389
|
+
}
|
|
390
|
+
function isCliRunManifestPathWithinRunsRoot(manifestPath, runsRoot) {
|
|
391
|
+
const relativePath = relative(runsRoot, manifestPath);
|
|
392
|
+
if (relativePath === '' || isAbsolute(relativePath)) {
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
const normalizedRelativePath = relativePath.replace(/\\/g, '/');
|
|
396
|
+
if (normalizedRelativePath.startsWith('../')) {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
const segments = normalizedRelativePath.split('/');
|
|
400
|
+
return (segments.length === 4 &&
|
|
401
|
+
segments[0].length > 0 &&
|
|
402
|
+
segments[1] === 'cli' &&
|
|
403
|
+
segments[2].length > 0 &&
|
|
404
|
+
segments[3] === 'manifest.json');
|
|
405
|
+
}
|
|
406
|
+
async function readSelectedRunManifestSnapshotInternal(context) {
|
|
407
|
+
const preferredSnapshot = await resolveProviderSelectedManifestSnapshot(context);
|
|
408
|
+
if (preferredSnapshot) {
|
|
409
|
+
return preferredSnapshot;
|
|
410
|
+
}
|
|
411
|
+
const manifest = await readJsonFile(context.paths.manifestPath);
|
|
412
|
+
if (!manifest) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
const control = context.controlStore.snapshot();
|
|
416
|
+
return buildSelectedRunManifestSnapshot(manifest, context.paths.manifestPath, control.run_id ?? null);
|
|
417
|
+
}
|
|
418
|
+
async function readManifestSnapshotForPath(manifestPath, fallbackRunId = null) {
|
|
419
|
+
const manifest = await readJsonFile(manifestPath);
|
|
420
|
+
if (!manifest) {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
return buildSelectedRunManifestSnapshot(manifest, manifestPath, readStringValue(manifest, 'run_id') ?? fallbackRunId);
|
|
424
|
+
}
|
|
425
|
+
function buildSelectedRunQuestionSummary(records) {
|
|
426
|
+
const queued = records.filter((record) => record.status === 'queued');
|
|
427
|
+
let latestQuestion = null;
|
|
428
|
+
for (const record of queued) {
|
|
429
|
+
if (!latestQuestion) {
|
|
430
|
+
latestQuestion = record;
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (Date.parse(record.queued_at) >= Date.parse(latestQuestion.queued_at)) {
|
|
434
|
+
latestQuestion = record;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return {
|
|
438
|
+
queuedCount: queued.length,
|
|
439
|
+
latestQuestion: latestQuestion
|
|
440
|
+
? {
|
|
441
|
+
questionId: latestQuestion.question_id,
|
|
442
|
+
prompt: latestQuestion.prompt,
|
|
443
|
+
urgency: latestQuestion.urgency,
|
|
444
|
+
queuedAt: latestQuestion.queued_at
|
|
445
|
+
}
|
|
446
|
+
: null
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function resolveSelectedRunDisplayStatus(input) {
|
|
450
|
+
if (input.terminalMergeCloseoutProgress) {
|
|
451
|
+
return {
|
|
452
|
+
displayStatus: input.terminalMergeCloseoutProgress.phase === 'pending_shared_root_reconciliation'
|
|
453
|
+
? 'pending_shared_root_reconciliation'
|
|
454
|
+
: 'failed',
|
|
455
|
+
statusReason: input.terminalMergeCloseoutProgress.stall_reason ?? null
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
if (input.rawStatus === 'in_progress' && input.latestAction === 'pause') {
|
|
459
|
+
return {
|
|
460
|
+
displayStatus: 'paused',
|
|
461
|
+
statusReason: input.questionSummary.queuedCount > 0 ? 'queued_questions' : 'control_pause'
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
if (input.rawStatus === 'in_progress' && input.questionSummary.queuedCount > 0) {
|
|
465
|
+
return { displayStatus: 'awaiting_input', statusReason: 'queued_questions' };
|
|
466
|
+
}
|
|
467
|
+
if (input.rawStatus === 'in_progress') {
|
|
468
|
+
const operatorVisibleState = resolveOperatorVisibleRunningState(input.compatibilityState);
|
|
469
|
+
if (operatorVisibleState) {
|
|
470
|
+
return { displayStatus: operatorVisibleState, statusReason: null };
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return { displayStatus: input.rawStatus, statusReason: null };
|
|
474
|
+
}
|
|
475
|
+
function resolveOperatorVisibleRunningState(compatibilityState) {
|
|
476
|
+
const normalizedState = compatibilityState?.state?.trim() ?? null;
|
|
477
|
+
const normalizedStateType = compatibilityState?.stateType?.trim().toLowerCase() ?? null;
|
|
478
|
+
if (!normalizedState) {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
const workflowState = classifyProviderLinearWorkflowState({
|
|
482
|
+
state: normalizedState,
|
|
483
|
+
state_type: normalizedStateType
|
|
484
|
+
});
|
|
485
|
+
if (workflowState.isTodo ||
|
|
486
|
+
workflowState.isTerminal ||
|
|
487
|
+
normalizedStateType === 'triage' ||
|
|
488
|
+
normalizedStateType === 'backlog' ||
|
|
489
|
+
normalizedStateType === 'unstarted') {
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
const stateKey = normalizedState.toLowerCase().replace(/[^a-z0-9]+/g, '');
|
|
493
|
+
if (stateKey === 'running' || stateKey === 'started') {
|
|
494
|
+
return normalizedState;
|
|
495
|
+
}
|
|
496
|
+
return normalizedState;
|
|
497
|
+
}
|
|
498
|
+
function buildSelectedRunLatestEvent(input) {
|
|
499
|
+
if (!input.controlAction &&
|
|
500
|
+
input.terminalMergeCloseoutProgress &&
|
|
501
|
+
input.providerDebugSnapshot?.progress) {
|
|
502
|
+
return {
|
|
503
|
+
at: input.providerDebugSnapshot.progress.summary_recorded_at ??
|
|
504
|
+
input.providerDebugSnapshot.progress.last_semantic_progress_at ??
|
|
505
|
+
input.providerDebugSnapshot.last_semantic_progress_at ??
|
|
506
|
+
input.updatedAt,
|
|
507
|
+
event: input.terminalMergeCloseoutProgress.phase,
|
|
508
|
+
message: input.terminalMergeCloseoutProgress.summary ?? input.summary,
|
|
509
|
+
source: input.terminalMergeCloseoutProgress.event_source ?? 'merge_closeout',
|
|
510
|
+
messageRecordedAt: input.terminalMergeCloseoutProgress.message_recorded_at ??
|
|
511
|
+
input.terminalMergeCloseoutProgress.summary_recorded_at ??
|
|
512
|
+
null,
|
|
513
|
+
sourceUpdatedAt: input.terminalMergeCloseoutProgress.source_updated_at ??
|
|
514
|
+
input.terminalMergeCloseoutProgress.last_semantic_progress_at ??
|
|
515
|
+
input.providerDebugSnapshot.last_semantic_progress_at ??
|
|
516
|
+
input.updatedAt,
|
|
517
|
+
candidates: input.terminalMergeCloseoutProgress.event_candidates ?? [],
|
|
518
|
+
requestedBy: null,
|
|
519
|
+
reason: input.terminalMergeCloseoutProgress.stall_reason ?? null
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
if (!input.controlAction &&
|
|
523
|
+
input.providerDebugSnapshot?.progress &&
|
|
524
|
+
hasAuthoritativeProviderDebugEvidence(input.providerDebugSnapshot) &&
|
|
525
|
+
shouldPreferProviderDebugProgressEvent(input.fallbackEvent)) {
|
|
526
|
+
return {
|
|
527
|
+
at: input.providerDebugSnapshot.progress.summary_recorded_at ??
|
|
528
|
+
input.providerDebugSnapshot.progress.message_recorded_at ??
|
|
529
|
+
input.providerDebugSnapshot.progress.source_updated_at ??
|
|
530
|
+
input.providerDebugSnapshot.progress.last_semantic_progress_at ??
|
|
531
|
+
input.providerDebugSnapshot.last_semantic_progress_at ??
|
|
532
|
+
input.updatedAt,
|
|
533
|
+
event: input.providerDebugSnapshot.progress.selected_event ??
|
|
534
|
+
input.providerDebugSnapshot.progress.phase,
|
|
535
|
+
message: input.providerDebugSnapshot.progress.summary ?? input.summary,
|
|
536
|
+
source: input.providerDebugSnapshot.progress.event_source ?? 'provider_debug_progress',
|
|
537
|
+
messageRecordedAt: input.providerDebugSnapshot.progress.message_recorded_at ??
|
|
538
|
+
input.providerDebugSnapshot.progress.summary_recorded_at ??
|
|
539
|
+
null,
|
|
540
|
+
sourceUpdatedAt: input.providerDebugSnapshot.progress.source_updated_at ??
|
|
541
|
+
input.providerDebugSnapshot.progress.last_semantic_progress_at ??
|
|
542
|
+
input.providerDebugSnapshot.last_semantic_progress_at ??
|
|
543
|
+
input.updatedAt,
|
|
544
|
+
candidates: input.providerDebugSnapshot.progress.event_candidates ?? [],
|
|
545
|
+
requestedBy: null,
|
|
546
|
+
reason: input.providerDebugSnapshot.progress.stall_reason ?? null
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
if (!input.controlAction && !input.updatedAt && !input.summary) {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
at: input.controlAction?.requested_at ?? input.updatedAt,
|
|
554
|
+
event: input.controlAction?.action ?? input.fallbackEvent,
|
|
555
|
+
message: input.summary,
|
|
556
|
+
source: input.controlAction ? 'control_action' : 'run_summary',
|
|
557
|
+
messageRecordedAt: null,
|
|
558
|
+
sourceUpdatedAt: input.controlAction?.requested_at ?? input.updatedAt,
|
|
559
|
+
candidates: [],
|
|
560
|
+
requestedBy: input.controlAction?.requested_by ?? null,
|
|
561
|
+
reason: input.controlAction?.reason ?? null
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
function hasAuthoritativeProviderDebugEvidence(providerDebugSnapshot) {
|
|
565
|
+
const claimIsStale = providerDebugSnapshot.claim?.freshness === 'stale';
|
|
566
|
+
if (claimIsStale && providerDebugSnapshot.progress?.kind === 'merge_closeout') {
|
|
567
|
+
return false;
|
|
568
|
+
}
|
|
569
|
+
const hasFreshClaim = providerDebugSnapshot.claim !== null && !claimIsStale;
|
|
570
|
+
const hasFreshPullRequest = providerDebugSnapshot.pull_request !== null && !claimIsStale;
|
|
571
|
+
return Boolean(hasFreshClaim ||
|
|
572
|
+
providerDebugSnapshot.worker ||
|
|
573
|
+
hasFreshPullRequest ||
|
|
574
|
+
providerDebugSnapshot.last_audit_operation);
|
|
575
|
+
}
|
|
576
|
+
function shouldPreferProviderDebugProgressEvent(fallbackEvent) {
|
|
577
|
+
return (fallbackEvent === 'in_progress' ||
|
|
578
|
+
fallbackEvent === 'running' ||
|
|
579
|
+
fallbackEvent === 'started' ||
|
|
580
|
+
fallbackEvent === 'resuming');
|
|
581
|
+
}
|
|
582
|
+
function resolveSelectedRunDisplaySummary(input) {
|
|
583
|
+
if (input.terminalMergeCloseoutProgress?.summary) {
|
|
584
|
+
return input.terminalMergeCloseoutProgress.summary;
|
|
585
|
+
}
|
|
586
|
+
const acceptedProviderRetryResumeAt = readLatestAcceptedProviderRetryResumeAt(input.manifestRecord);
|
|
587
|
+
const hasFailedCommandsForAuthoritativeAttempt = acceptedProviderRetryResumeAt === null
|
|
588
|
+
? manifestHasFailedCommands(input.manifestRecord)
|
|
589
|
+
: manifestHasFailedCommandsSince(input.manifestRecord, acceptedProviderRetryResumeAt);
|
|
590
|
+
if (input.rawStatus === 'succeeded' &&
|
|
591
|
+
input.summary &&
|
|
592
|
+
hasStaleFailureSummary(input.summary, input.manifestRecord) &&
|
|
593
|
+
!hasFailedCommandsForAuthoritativeAttempt) {
|
|
594
|
+
const filteredSummary = filterStaleFailureSummary(input.summary, input.manifestRecord);
|
|
595
|
+
return filteredSummary ?? 'Completed successfully';
|
|
596
|
+
}
|
|
597
|
+
if (input.rawStatus === 'in_progress' &&
|
|
598
|
+
input.summary &&
|
|
599
|
+
hasStaleFailureSummary(input.summary, input.manifestRecord) &&
|
|
600
|
+
acceptedProviderRetryResumeAt !== null &&
|
|
601
|
+
!manifestHasFailedCommandsSince(input.manifestRecord, acceptedProviderRetryResumeAt)) {
|
|
602
|
+
const filteredSummary = filterStaleFailureSummary(input.summary, input.manifestRecord);
|
|
603
|
+
return filteredSummary ?? 'Retry accepted; run resumed after a failed attempt.';
|
|
604
|
+
}
|
|
605
|
+
return input.summary;
|
|
606
|
+
}
|
|
607
|
+
function resolveTerminalMergeCloseoutProgress(input) {
|
|
608
|
+
const progress = input.providerDebugSnapshot?.progress ?? null;
|
|
609
|
+
if (input.rawStatus !== 'succeeded' ||
|
|
610
|
+
!progress ||
|
|
611
|
+
progress.kind !== 'merge_closeout' ||
|
|
612
|
+
!input.providerDebugSnapshot) {
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
const isTerminalMergeCloseoutProgress = progress.phase === 'pending_shared_root_reconciliation' ||
|
|
616
|
+
progress.status === 'failed';
|
|
617
|
+
if (!isTerminalMergeCloseoutProgress) {
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
if (hasAuthoritativeProviderDebugEvidence(input.providerDebugSnapshot)) {
|
|
621
|
+
return progress;
|
|
622
|
+
}
|
|
623
|
+
if (input.providerDebugSnapshot.claim?.freshness !== 'stale') {
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
if (input.trackedIssue && classifyProviderLinearWorkflowState(input.trackedIssue).isTerminal) {
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
return progress;
|
|
630
|
+
}
|
|
631
|
+
function isTerminalRunStatus(status) {
|
|
632
|
+
return status === 'succeeded' || status === 'failed' || status === 'cancelled' || status === 'canceled';
|
|
633
|
+
}
|
|
634
|
+
function hasStaleFailureSummary(summary, manifestRecord) {
|
|
635
|
+
return summary.split('\n').some((line) => isStaleFailureSummaryLine(line, manifestRecord));
|
|
636
|
+
}
|
|
637
|
+
function filterStaleFailureSummary(summary, manifestRecord) {
|
|
638
|
+
const retainedLines = summary
|
|
639
|
+
.split('\n')
|
|
640
|
+
.map((line) => line.trim())
|
|
641
|
+
.filter((line) => line.length > 0 && !isStaleFailureSummaryLine(line, manifestRecord));
|
|
642
|
+
if (retainedLines.length === 0) {
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
return retainedLines.join('\n');
|
|
646
|
+
}
|
|
647
|
+
function isStaleFailureSummaryLine(line, manifestRecord) {
|
|
648
|
+
const trimmed = line.trim();
|
|
649
|
+
if (/^Stage '.*' failed with exit code \d+\.$/u.test(trimmed) ||
|
|
650
|
+
/^Sub-pipeline '.*' failed\.$/u.test(trimmed) ||
|
|
651
|
+
/^Execution error: .+/u.test(trimmed)) {
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
654
|
+
if (!/^Sub-pipeline error: .+/u.test(trimmed)) {
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
return !hasMatchingSkippedSubpipelineErrorSummary(manifestRecord, trimmed);
|
|
658
|
+
}
|
|
659
|
+
function manifestHasFailedCommands(manifestRecord) {
|
|
660
|
+
const commands = manifestRecord.commands;
|
|
661
|
+
if (!Array.isArray(commands)) {
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
return commands.some((command) => {
|
|
665
|
+
if (!command || typeof command !== 'object') {
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
668
|
+
return readStringValue(command, 'status') === 'failed';
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
function manifestHasFailedCommandsSince(manifestRecord, sinceAtMs) {
|
|
672
|
+
const commands = manifestRecord.commands;
|
|
673
|
+
if (!Array.isArray(commands)) {
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
return commands.some((command) => {
|
|
677
|
+
if (!isRecord(command) || readStringValue(command, 'status') !== 'failed') {
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
const commandFailureAtMs = readLatestCommandFailureTimestampMs(command);
|
|
681
|
+
if (commandFailureAtMs === null) {
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
return commandFailureAtMs >= sinceAtMs;
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
function readLatestAcceptedProviderRetryResumeAt(manifestRecord) {
|
|
688
|
+
const resumeEvents = manifestRecord.resume_events;
|
|
689
|
+
if (!Array.isArray(resumeEvents)) {
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
let latestAcceptedAt = null;
|
|
693
|
+
for (const event of resumeEvents) {
|
|
694
|
+
if (!isRecord(event) ||
|
|
695
|
+
readStringValue(event, 'reason') !== 'provider-retry' ||
|
|
696
|
+
readStringValue(event, 'outcome') !== 'accepted') {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
const acceptedAt = Date.parse(readStringValue(event, 'timestamp') ?? '');
|
|
700
|
+
if (!Number.isFinite(acceptedAt)) {
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
latestAcceptedAt = latestAcceptedAt === null ? acceptedAt : Math.max(latestAcceptedAt, acceptedAt);
|
|
704
|
+
}
|
|
705
|
+
return latestAcceptedAt;
|
|
706
|
+
}
|
|
707
|
+
function readLatestCommandFailureTimestampMs(command) {
|
|
708
|
+
return readLatestCommandTimestampMs(command);
|
|
709
|
+
}
|
|
710
|
+
function hasMatchingSkippedSubpipelineErrorSummary(manifestRecord, summaryLine) {
|
|
711
|
+
const commands = manifestRecord.commands;
|
|
712
|
+
if (!Array.isArray(commands)) {
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
return commands.some((command) => {
|
|
716
|
+
if (!isRecord(command) || readStringValue(command, 'status') !== 'skipped') {
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
if ((readStringValue(command, 'summary') ?? '').trim() !== summaryLine) {
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
return true;
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
function readLatestCommandTimestampMs(command) {
|
|
726
|
+
for (const key of ['completed_at', 'completedAt', 'updated_at', 'updatedAt', 'started_at', 'startedAt']) {
|
|
727
|
+
const value = readStringValue(command, key);
|
|
728
|
+
const parsed = Date.parse(value ?? '');
|
|
729
|
+
if (Number.isFinite(parsed)) {
|
|
730
|
+
return parsed;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
function readManifestStageSummaries(manifestRecord) {
|
|
736
|
+
const commands = manifestRecord.commands;
|
|
737
|
+
if (!Array.isArray(commands)) {
|
|
738
|
+
return [];
|
|
739
|
+
}
|
|
740
|
+
return commands.flatMap((command) => {
|
|
741
|
+
if (!isRecord(command)) {
|
|
742
|
+
return [];
|
|
743
|
+
}
|
|
744
|
+
const id = readStringValue(command, 'id');
|
|
745
|
+
if (!id) {
|
|
746
|
+
return [];
|
|
747
|
+
}
|
|
748
|
+
return [
|
|
749
|
+
{
|
|
750
|
+
id,
|
|
751
|
+
title: readStringValue(command, 'title') ?? id,
|
|
752
|
+
status: readStringValue(command, 'status') ?? null
|
|
753
|
+
}
|
|
754
|
+
];
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
function readManifestApprovalsTotal(manifestRecord) {
|
|
758
|
+
return Array.isArray(manifestRecord.approvals) ? manifestRecord.approvals.length : 0;
|
|
759
|
+
}
|
|
760
|
+
async function resolveControlWorkspacePath(context) {
|
|
761
|
+
const controlManifest = await readJsonFile(context.paths.manifestPath);
|
|
762
|
+
const explicitWorkspacePath = controlManifest ? resolveManifestWorkspacePath(controlManifest) : null;
|
|
763
|
+
if (explicitWorkspacePath) {
|
|
764
|
+
return explicitWorkspacePath;
|
|
765
|
+
}
|
|
766
|
+
return resolveSafeLegacyWorkspacePathFromRunDir(context.paths.runDir);
|
|
767
|
+
}
|
|
768
|
+
function resolveSafeLegacyWorkspacePathFromRunDir(runDir) {
|
|
769
|
+
const legacyWorkspacePath = resolveLegacyWorkspacePathFromRunDir(runDir);
|
|
770
|
+
const runsRoot = resolveRunsRootFromRunDir(runDir);
|
|
771
|
+
if (!runsRoot) {
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
774
|
+
return normalizePathForComparison(runsRoot) === normalizePathForComparison(join(legacyWorkspacePath, '.runs'))
|
|
775
|
+
? legacyWorkspacePath
|
|
776
|
+
: null;
|
|
777
|
+
}
|
|
778
|
+
function isRecord(value) {
|
|
779
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
780
|
+
}
|
|
781
|
+
function resolveRunsRootFromRunDir(runDir) {
|
|
782
|
+
const candidate = resolve(runDir, '..', '..', '..');
|
|
783
|
+
return candidate || null;
|
|
784
|
+
}
|
|
785
|
+
function resolveTaskIdFromManifestPath(manifestPath) {
|
|
786
|
+
const segments = normalizePathForComparison(manifestPath).split('/').filter((segment) => segment.length > 0);
|
|
787
|
+
const manifestIndex = segments[segments.length - 1] === 'manifest.json' ? segments.length - 1 : segments.length;
|
|
788
|
+
if (manifestIndex < 3) {
|
|
789
|
+
return null;
|
|
790
|
+
}
|
|
791
|
+
if (segments[manifestIndex - 2] !== 'cli') {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
return segments[manifestIndex - 3] ?? null;
|
|
795
|
+
}
|
|
796
|
+
function resolveRunIdFromManifestPath(manifestPath) {
|
|
797
|
+
const normalizedPath = manifestPath.replace(/\\/g, '/');
|
|
798
|
+
const match = normalizedPath.match(/\/cli\/([^/]+)\/manifest\.json$/);
|
|
799
|
+
return match?.[1] ?? null;
|
|
800
|
+
}
|
|
801
|
+
function normalizePathForComparison(pathname) {
|
|
802
|
+
return resolve(pathname).replace(/\\/g, '/');
|
|
803
|
+
}
|
|
804
|
+
async function resolveProjectionContextParts(context, snapshot) {
|
|
805
|
+
if (!snapshot || snapshot.runDir === context.paths.runDir) {
|
|
806
|
+
return {
|
|
807
|
+
control: context.controlStore.snapshot(),
|
|
808
|
+
questions: context.questionQueue.list(),
|
|
809
|
+
runDir: context.paths.runDir,
|
|
810
|
+
trackedIssue: selectFreshLinearAdvisoryTrackedIssue(context.linearAdvisoryState),
|
|
811
|
+
providerLinearWorkerProof: await readProviderLinearWorkerProofForProjection(context.paths.runDir)
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
const control = normalizeControlState(await readJsonFile(join(snapshot.runDir, 'control.json')), snapshot.runId);
|
|
815
|
+
const questionSnapshot = await readJsonFile(join(snapshot.runDir, 'questions.json'));
|
|
816
|
+
const advisoryState = await readJsonFile(join(snapshot.runDir, LINEAR_ADVISORY_STATE_FILE));
|
|
817
|
+
return {
|
|
818
|
+
control,
|
|
819
|
+
questions: Array.isArray(questionSnapshot?.questions) ? questionSnapshot.questions : [],
|
|
820
|
+
runDir: snapshot.runDir,
|
|
821
|
+
trackedIssue: selectFreshLinearAdvisoryTrackedIssue(advisoryState),
|
|
822
|
+
providerLinearWorkerProof: await readProviderLinearWorkerProofForProjection(snapshot.runDir)
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
function buildSelectedRunManifestSnapshot(manifestRecord, manifestPath, fallbackRunId) {
|
|
826
|
+
const issueProvider = readStringValue(manifestRecord, 'issue_provider', 'issueProvider') ?? null;
|
|
827
|
+
const taskId = readStringValue(manifestRecord, 'task_id', 'taskId') ?? resolveTaskIdFromManifestPath(manifestPath);
|
|
828
|
+
const runId = readStringValue(manifestRecord, 'run_id', 'runId') ??
|
|
829
|
+
fallbackRunId ??
|
|
830
|
+
resolveRunIdFromManifestPath(manifestPath);
|
|
831
|
+
const issueIdentifier = readStringValue(manifestRecord, 'issue_identifier', 'issueIdentifier') ?? taskId ?? runId;
|
|
832
|
+
const issueId = readStringValue(manifestRecord, 'issue_id', 'issueId') ?? taskId ?? runId;
|
|
833
|
+
if (!issueIdentifier) {
|
|
834
|
+
return null;
|
|
835
|
+
}
|
|
836
|
+
return {
|
|
837
|
+
manifestRecord,
|
|
838
|
+
manifestPath,
|
|
839
|
+
runDir: dirname(manifestPath),
|
|
840
|
+
issueProvider,
|
|
841
|
+
issueIdentifier,
|
|
842
|
+
issueId,
|
|
843
|
+
taskId,
|
|
844
|
+
runId,
|
|
845
|
+
lookupAliases: buildProjectionLookupAliases({
|
|
846
|
+
issueIdentifier,
|
|
847
|
+
issueId,
|
|
848
|
+
taskId,
|
|
849
|
+
runId
|
|
850
|
+
})
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
function normalizeControlState(snapshot, runId) {
|
|
854
|
+
return {
|
|
855
|
+
run_id: snapshot?.run_id ?? runId ?? 'unknown-run',
|
|
856
|
+
control_seq: typeof snapshot?.control_seq === 'number' ? snapshot.control_seq : 0,
|
|
857
|
+
latest_action: snapshot?.latest_action ?? null,
|
|
858
|
+
feature_toggles: snapshot?.feature_toggles ?? null,
|
|
859
|
+
transport_mutation: snapshot?.transport_mutation ?? null
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
async function readTaskCompatibilityContexts(cliRoot, options = {}) {
|
|
863
|
+
const discovered = [];
|
|
864
|
+
const runEntries = await readDirectoryNames(cliRoot);
|
|
865
|
+
for (const runEntry of runEntries.sort((left, right) => left.localeCompare(right)).reverse()) {
|
|
866
|
+
if (options.excludeRunId && runEntry === options.excludeRunId) {
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
const runDir = join(cliRoot, runEntry);
|
|
870
|
+
const manifestPath = join(runDir, 'manifest.json');
|
|
871
|
+
const manifest = await readJsonFile(manifestPath);
|
|
872
|
+
if (!manifest) {
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
const snapshot = buildSelectedRunManifestSnapshot(manifest, manifestPath, runEntry);
|
|
876
|
+
if (!snapshot) {
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
if (isAuxiliaryProviderProofHarnessManifest(manifest)) {
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
const control = normalizeControlState(await readJsonFile(join(runDir, 'control.json')), snapshot.runId);
|
|
883
|
+
const questionSnapshot = await readJsonFile(join(runDir, 'questions.json'));
|
|
884
|
+
const advisoryState = await readJsonFile(join(runDir, LINEAR_ADVISORY_STATE_FILE));
|
|
885
|
+
const providerLinearWorkerProof = await readProviderLinearWorkerProofForProjection(runDir);
|
|
886
|
+
const context = buildProjectionContextFromParts(snapshot, {
|
|
887
|
+
control,
|
|
888
|
+
questions: Array.isArray(questionSnapshot?.questions) ? questionSnapshot.questions : [],
|
|
889
|
+
runDir,
|
|
890
|
+
trackedIssue: selectFreshLinearAdvisoryTrackedIssue(advisoryState),
|
|
891
|
+
providerLinearWorkerProof
|
|
892
|
+
}, resolveRunsRootFromRunDir(runDir), options.controlWorkspacePath ?? resolveSafeLegacyWorkspacePathFromRunDir(runDir), findMatchingProviderIntakeClaim(options.providerIntakeState, snapshot, providerLinearWorkerProof), options.providerIntakeState ?? null);
|
|
893
|
+
if (context) {
|
|
894
|
+
discovered.push({
|
|
895
|
+
context,
|
|
896
|
+
retryFallbackEligible: isManifestRetryFallbackCandidate(manifest)
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
return discovered;
|
|
901
|
+
}
|
|
902
|
+
async function reconcileProviderLinearWorkerDiscoveredContexts(discovered, providerIntakeState, additionalContexts = []) {
|
|
903
|
+
if (!providerIntakeState) {
|
|
904
|
+
return discovered;
|
|
905
|
+
}
|
|
906
|
+
const discoveredContexts = discovered.map((candidate) => candidate.context);
|
|
907
|
+
const allContexts = additionalContexts.concat(discoveredContexts);
|
|
908
|
+
return await Promise.all(discovered.map(async (entry) => {
|
|
909
|
+
const reconciliation = resolveProviderLinearWorkerRunArtifactReconciliation(entry.context, allContexts, providerIntakeState);
|
|
910
|
+
if (!reconciliation) {
|
|
911
|
+
return entry;
|
|
912
|
+
}
|
|
913
|
+
await writeProviderLinearWorkerRunArtifactReconciliation(entry.context.runDir, reconciliation).catch(() => undefined);
|
|
914
|
+
return {
|
|
915
|
+
...entry,
|
|
916
|
+
context: applyProviderLinearWorkerRunArtifactReconciliation(entry.context, reconciliation)
|
|
917
|
+
};
|
|
918
|
+
}));
|
|
919
|
+
}
|
|
920
|
+
function resolveProviderLinearWorkerRunArtifactReconciliation(context, allContexts, providerIntakeState) {
|
|
921
|
+
if (!isProviderLinearWorkerReconciliationSource(context) ||
|
|
922
|
+
!isActiveLookingProviderLinearWorkerManifestStatus(context.rawStatus)) {
|
|
923
|
+
return null;
|
|
924
|
+
}
|
|
925
|
+
const claim = findProviderLinearWorkerClaimForContext(providerIntakeState, context);
|
|
926
|
+
if (claim && isActiveProviderLinearWorkerClaimState(claim.state)) {
|
|
927
|
+
return null;
|
|
928
|
+
}
|
|
929
|
+
const replacementRun = findNewerTerminalProviderLinearWorkerContext(allContexts, context);
|
|
930
|
+
const supersedingRunBoundClaim = !claim
|
|
931
|
+
? findNewerRunBoundProviderLinearWorkerClaimForContext(providerIntakeState, context)
|
|
932
|
+
: null;
|
|
933
|
+
const claimReconciliationReason = claim
|
|
934
|
+
? resolveProviderLinearWorkerClaimReconciliationReason(claim)
|
|
935
|
+
: null;
|
|
936
|
+
const absentClaimReconciliationReason = !claim
|
|
937
|
+
? resolveAbsentProviderLinearWorkerClaimReconciliationReason(providerIntakeState, context, replacementRun, supersedingRunBoundClaim)
|
|
938
|
+
: null;
|
|
939
|
+
const reason = claimReconciliationReason ?? absentClaimReconciliationReason;
|
|
940
|
+
if (!reason) {
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
const claimForRecord = claim ?? supersedingRunBoundClaim;
|
|
944
|
+
const evidenceUpdatedAt = selectProviderLinearWorkerReconciliationEvidenceUpdatedAt(reason, claimForRecord, replacementRun, providerIntakeState);
|
|
945
|
+
if (!isProviderLinearWorkerReconciliationEvidenceNewerThanContext(evidenceUpdatedAt, context, Boolean(replacementRun) || reason === 'provider_claim_active_newer_run')) {
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
const reconciledStatus = replacementRun?.rawStatus ??
|
|
949
|
+
(claim?.state === 'completed' ? 'succeeded' : 'cancelled');
|
|
950
|
+
const recordedAt = evidenceUpdatedAt ?? context.updatedAt ?? context.startedAt ?? new Date(0).toISOString();
|
|
951
|
+
const supersedingRunBoundClaimSummaryPrefix = supersedingRunBoundClaim && isActiveProviderLinearWorkerClaimState(supersedingRunBoundClaim.state)
|
|
952
|
+
? 'newer active claim run'
|
|
953
|
+
: 'newer run-bound claim run';
|
|
954
|
+
const summary = replacementRun
|
|
955
|
+
? `Provider worker artifact reconciled as ${reconciledStatus}: newer terminal run ${replacementRun.runId ?? 'unknown'} supersedes retained ${context.rawStatus} manifest.`
|
|
956
|
+
: supersedingRunBoundClaim
|
|
957
|
+
? `Provider worker artifact reconciled as ${reconciledStatus}: ${supersedingRunBoundClaimSummaryPrefix} ${supersedingRunBoundClaim.run_id ?? 'unknown'} supersedes retained ${context.rawStatus} manifest.`
|
|
958
|
+
: reason === 'provider_issue_removed'
|
|
959
|
+
? `Provider worker artifact reconciled as ${reconciledStatus}: provider intake removed this issue with no active claim.`
|
|
960
|
+
: `Provider worker artifact reconciled as ${reconciledStatus}: provider claim is ${claimForRecord?.state ?? 'absent'} (${claimForRecord?.reason ?? reason}).`;
|
|
961
|
+
return {
|
|
962
|
+
schema_version: 1,
|
|
963
|
+
kind: 'provider-linear-worker-run-artifact-reconciliation',
|
|
964
|
+
status: 'reconciled',
|
|
965
|
+
reconciled_status: reconciledStatus,
|
|
966
|
+
reason,
|
|
967
|
+
summary,
|
|
968
|
+
recorded_at: recordedAt,
|
|
969
|
+
manifest: {
|
|
970
|
+
path: context.manifestPath ?? null,
|
|
971
|
+
run_id: context.runId,
|
|
972
|
+
task_id: context.taskId,
|
|
973
|
+
status: context.rawStatus,
|
|
974
|
+
updated_at: context.updatedAt
|
|
975
|
+
},
|
|
976
|
+
provider_claim: claimForRecord
|
|
977
|
+
? {
|
|
978
|
+
state: claimForRecord.state,
|
|
979
|
+
reason: claimForRecord.reason,
|
|
980
|
+
issue_state: claimForRecord.issue_state,
|
|
981
|
+
issue_state_type: claimForRecord.issue_state_type,
|
|
982
|
+
updated_at: claimForRecord.updated_at,
|
|
983
|
+
run_id: claimForRecord.run_id,
|
|
984
|
+
run_manifest_path: claimForRecord.run_manifest_path
|
|
985
|
+
}
|
|
986
|
+
: null,
|
|
987
|
+
replacement_run: replacementRun
|
|
988
|
+
? {
|
|
989
|
+
run_id: replacementRun.runId,
|
|
990
|
+
status: replacementRun.rawStatus,
|
|
991
|
+
manifest_path: replacementRun.manifestPath ?? null,
|
|
992
|
+
updated_at: replacementRun.updatedAt
|
|
993
|
+
}
|
|
994
|
+
: null
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
function applyProviderLinearWorkerRunArtifactReconciliation(context, reconciliation) {
|
|
998
|
+
return {
|
|
999
|
+
...context,
|
|
1000
|
+
rawStatus: reconciliation.reconciled_status,
|
|
1001
|
+
displayStatus: reconciliation.reconciled_status,
|
|
1002
|
+
statusReason: reconciliation.reason,
|
|
1003
|
+
completedAt: context.completedAt ?? reconciliation.recorded_at,
|
|
1004
|
+
summary: reconciliation.summary,
|
|
1005
|
+
lastError: reconciliation.reconciled_status === 'failed'
|
|
1006
|
+
? context.lastError ?? reconciliation.summary
|
|
1007
|
+
: context.lastError,
|
|
1008
|
+
latestEvent: {
|
|
1009
|
+
...(context.latestEvent ?? {
|
|
1010
|
+
requestedBy: null,
|
|
1011
|
+
reason: null
|
|
1012
|
+
}),
|
|
1013
|
+
at: reconciliation.recorded_at,
|
|
1014
|
+
event: reconciliation.reconciled_status,
|
|
1015
|
+
message: reconciliation.summary,
|
|
1016
|
+
reason: reconciliation.reason
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
async function writeProviderLinearWorkerRunArtifactReconciliation(runDir, reconciliation) {
|
|
1021
|
+
if (!runDir) {
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
const targetPath = join(runDir, PROVIDER_LINEAR_WORKER_RECONCILIATION_FILENAME);
|
|
1025
|
+
const existing = await readJsonFile(targetPath);
|
|
1026
|
+
if (JSON.stringify(existing) === JSON.stringify(reconciliation)) {
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
await writeJsonAtomic(targetPath, reconciliation);
|
|
1030
|
+
}
|
|
1031
|
+
function isActiveLookingProviderLinearWorkerManifestStatus(status) {
|
|
1032
|
+
return status === 'in_progress' || status === 'launching';
|
|
1033
|
+
}
|
|
1034
|
+
function isProviderLinearWorkerReconciliationSource(context) {
|
|
1035
|
+
if (context.issueProvider !== null && context.issueProvider !== 'linear') {
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
1038
|
+
return (context.pipelineId === PROVIDER_LINEAR_WORKER_PIPELINE_ID ||
|
|
1039
|
+
context.pipelineTitle === PROVIDER_LINEAR_WORKER_PIPELINE_TITLE ||
|
|
1040
|
+
context.providerLinearWorkerProof != null);
|
|
1041
|
+
}
|
|
1042
|
+
function findProviderLinearWorkerClaimForContext(providerIntakeState, context) {
|
|
1043
|
+
const candidates = providerIntakeState.claims.filter((claim) => providerLinearWorkerClaimMatchesContext(claim, context));
|
|
1044
|
+
return candidates.sort((left, right) => compareProviderLinearWorkerClaimForContext(left, right, context))[0] ?? null;
|
|
1045
|
+
}
|
|
1046
|
+
function compareProviderLinearWorkerClaimForContext(left, right, context) {
|
|
1047
|
+
const leftRunIdentityMatch = providerLinearWorkerClaimRunIdentityMatchesContext(left, context);
|
|
1048
|
+
const rightRunIdentityMatch = providerLinearWorkerClaimRunIdentityMatchesContext(right, context);
|
|
1049
|
+
if (leftRunIdentityMatch !== rightRunIdentityMatch) {
|
|
1050
|
+
return leftRunIdentityMatch ? -1 : 1;
|
|
1051
|
+
}
|
|
1052
|
+
return compareIsoTimestamp(right.updated_at, left.updated_at);
|
|
1053
|
+
}
|
|
1054
|
+
function providerLinearWorkerClaimMatchesContext(claim, context) {
|
|
1055
|
+
if (claim.provider !== 'linear') {
|
|
1056
|
+
return false;
|
|
1057
|
+
}
|
|
1058
|
+
if (providerLinearWorkerClaimRunIdentityMatchesContext(claim, context)) {
|
|
1059
|
+
return true;
|
|
1060
|
+
}
|
|
1061
|
+
if (isProviderLinearWorkerRunBoundClaimAuthoritative(claim) &&
|
|
1062
|
+
providerLinearWorkerClaimHasRunIdentity(claim)) {
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
return providerLinearWorkerClaimIssueIdentityMatchesContext(claim, context);
|
|
1066
|
+
}
|
|
1067
|
+
function isActiveProviderLinearWorkerClaimState(state) {
|
|
1068
|
+
return (state === 'accepted' ||
|
|
1069
|
+
state === 'starting' ||
|
|
1070
|
+
state === 'running' ||
|
|
1071
|
+
state === 'resuming' ||
|
|
1072
|
+
state === 'resumable' ||
|
|
1073
|
+
state === 'handoff_failed');
|
|
1074
|
+
}
|
|
1075
|
+
function resolveProviderLinearWorkerClaimReconciliationReason(claim) {
|
|
1076
|
+
if (claim.state === 'completed') {
|
|
1077
|
+
return 'provider_claim_completed';
|
|
1078
|
+
}
|
|
1079
|
+
if (claim.state === 'released') {
|
|
1080
|
+
const reason = claim.reason ?? '';
|
|
1081
|
+
if (isLiveRehydrateProviderLinearWorkerReleasedClaim(claim)) {
|
|
1082
|
+
return null;
|
|
1083
|
+
}
|
|
1084
|
+
if (reason.includes('not_active') ||
|
|
1085
|
+
reason.includes('not_mutable') ||
|
|
1086
|
+
reason.includes('assignee_changed') ||
|
|
1087
|
+
reason.includes('todo_blocked_by_non_terminal') ||
|
|
1088
|
+
isTerminalProviderLinearIssueState(claim.issue_state, claim.issue_state_type)) {
|
|
1089
|
+
return 'provider_claim_released';
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (claim.state === 'stale' || claim.state === 'duplicate' || claim.state === 'ignored') {
|
|
1093
|
+
return `provider_claim_${claim.state}`;
|
|
1094
|
+
}
|
|
1095
|
+
return null;
|
|
1096
|
+
}
|
|
1097
|
+
function isLiveRehydrateProviderLinearWorkerReleasedClaim(claim) {
|
|
1098
|
+
if (!isProviderLinearWorkerReleasedLiveRehydrateReason(claim.reason)) {
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
const workflowState = classifyProviderLinearWorkflowState({
|
|
1102
|
+
state: claim.issue_state,
|
|
1103
|
+
state_type: claim.issue_state_type
|
|
1104
|
+
});
|
|
1105
|
+
return workflowState.isActive && !workflowState.isTodo;
|
|
1106
|
+
}
|
|
1107
|
+
function isProviderLinearWorkerReleasedLiveRehydrateReason(reason) {
|
|
1108
|
+
return (reason === 'provider_issue_released:not_active' ||
|
|
1109
|
+
isProviderLinearWorkerReleasedPendingReopenReason(reason));
|
|
1110
|
+
}
|
|
1111
|
+
function isProviderLinearWorkerReleasedPendingReopenReason(reason) {
|
|
1112
|
+
return (typeof reason === 'string' &&
|
|
1113
|
+
reason.startsWith('provider_issue_released_pending_reopen:'));
|
|
1114
|
+
}
|
|
1115
|
+
function resolveAbsentProviderLinearWorkerClaimReconciliationReason(providerIntakeState, context, replacementRun, supersedingActiveClaim) {
|
|
1116
|
+
if (replacementRun) {
|
|
1117
|
+
return 'provider_claim_absent_newer_terminal_run';
|
|
1118
|
+
}
|
|
1119
|
+
if (supersedingActiveClaim) {
|
|
1120
|
+
return 'provider_claim_active_newer_run';
|
|
1121
|
+
}
|
|
1122
|
+
if (providerLinearWorkerRemovedIntakeReasonMatchesContext(providerIntakeState, context)) {
|
|
1123
|
+
return 'provider_issue_removed';
|
|
1124
|
+
}
|
|
1125
|
+
return null;
|
|
1126
|
+
}
|
|
1127
|
+
function providerLinearWorkerRemovedIntakeReasonMatchesContext(providerIntakeState, context) {
|
|
1128
|
+
if (providerIntakeState.latest_reason !== 'provider_issue_removed') {
|
|
1129
|
+
return false;
|
|
1130
|
+
}
|
|
1131
|
+
const providerIssueKeys = buildProviderLinearWorkerContextProviderIssueKeys(context);
|
|
1132
|
+
return Boolean(providerIntakeState.latest_provider_key &&
|
|
1133
|
+
providerIssueKeys.includes(providerIntakeState.latest_provider_key));
|
|
1134
|
+
}
|
|
1135
|
+
function buildProviderLinearWorkerContextProviderIssueKeys(context) {
|
|
1136
|
+
const identities = [context.issueId, context.issueIdentifier].filter((value) => Boolean(value && !isProjectionFallbackIdentityValue(value, context)));
|
|
1137
|
+
return Array.from(new Set(identities.map((identity) => buildProviderIssueKey('linear', identity))));
|
|
1138
|
+
}
|
|
1139
|
+
function selectProviderLinearWorkerReconciliationEvidenceUpdatedAt(reason, claim, replacementRun, providerIntakeState) {
|
|
1140
|
+
const replacementRunEvidenceAt = replacementRun
|
|
1141
|
+
? selectProviderLinearWorkerReconciliationRunEvidenceTimestamp(replacementRun)
|
|
1142
|
+
: null;
|
|
1143
|
+
const claimRunEvidenceAt = claim
|
|
1144
|
+
? selectProviderLinearWorkerClaimRunEvidenceTimestamp(claim)
|
|
1145
|
+
: null;
|
|
1146
|
+
if (reason === 'provider_claim_absent_newer_terminal_run') {
|
|
1147
|
+
return replacementRunEvidenceAt;
|
|
1148
|
+
}
|
|
1149
|
+
if (reason === 'provider_issue_removed') {
|
|
1150
|
+
return providerIntakeState.updated_at;
|
|
1151
|
+
}
|
|
1152
|
+
return selectLatestIsoTimestamp(claim?.updated_at ?? null, claimRunEvidenceAt, replacementRunEvidenceAt);
|
|
1153
|
+
}
|
|
1154
|
+
function selectProviderLinearWorkerReconciliationRunEvidenceTimestamp(context) {
|
|
1155
|
+
return selectLatestIsoTimestamp(context.updatedAt, selectProviderLinearWorkerContextChronologyTimestamp(context));
|
|
1156
|
+
}
|
|
1157
|
+
function isProviderLinearWorkerReconciliationEvidenceNewerThanContext(evidenceUpdatedAt, context, useRunChronologyBoundary = false) {
|
|
1158
|
+
if (!evidenceUpdatedAt) {
|
|
1159
|
+
return false;
|
|
1160
|
+
}
|
|
1161
|
+
const contextEvidenceBoundary = useRunChronologyBoundary
|
|
1162
|
+
? selectProviderLinearWorkerContextChronologyTimestamp(context) ?? context.updatedAt
|
|
1163
|
+
: selectLatestIsoTimestamp(context.updatedAt, context.startedAt);
|
|
1164
|
+
return !contextEvidenceBoundary || compareIsoTimestamp(evidenceUpdatedAt, contextEvidenceBoundary) > 0;
|
|
1165
|
+
}
|
|
1166
|
+
function isTerminalProviderLinearIssueState(issueState, issueStateType) {
|
|
1167
|
+
return classifyProviderLinearWorkflowState({
|
|
1168
|
+
state: issueState,
|
|
1169
|
+
state_type: issueStateType
|
|
1170
|
+
}).isTerminal;
|
|
1171
|
+
}
|
|
1172
|
+
function findNewerTerminalProviderLinearWorkerContext(contexts, context) {
|
|
1173
|
+
return contexts
|
|
1174
|
+
.filter((candidate) => candidate !== context &&
|
|
1175
|
+
isProviderLinearWorkerReconciliationSource(candidate) &&
|
|
1176
|
+
isTerminalRunStatus(candidate.rawStatus) &&
|
|
1177
|
+
providerLinearWorkerContextsShareIssueIdentity(candidate, context) &&
|
|
1178
|
+
isProviderLinearWorkerContextChronologicallyNewer(candidate, context))
|
|
1179
|
+
.sort(compareProviderLinearWorkerContextChronologyDesc)[0] ?? null;
|
|
1180
|
+
}
|
|
1181
|
+
function compareProviderLinearWorkerContextChronologyDesc(left, right) {
|
|
1182
|
+
const chronologyComparison = compareIsoTimestamp(selectProviderLinearWorkerContextChronologyTimestamp(right), selectProviderLinearWorkerContextChronologyTimestamp(left));
|
|
1183
|
+
return chronologyComparison !== 0
|
|
1184
|
+
? chronologyComparison
|
|
1185
|
+
: compareIsoTimestamp(right.updatedAt, left.updatedAt);
|
|
1186
|
+
}
|
|
1187
|
+
function isProviderLinearWorkerContextChronologicallyNewer(candidate, context) {
|
|
1188
|
+
const candidateStartedAt = selectProviderLinearWorkerContextChronologyTimestamp(candidate);
|
|
1189
|
+
const contextStartedAt = selectProviderLinearWorkerContextChronologyTimestamp(context);
|
|
1190
|
+
if (!candidateStartedAt) {
|
|
1191
|
+
return false;
|
|
1192
|
+
}
|
|
1193
|
+
if (!contextStartedAt) {
|
|
1194
|
+
const contextUpdatedAt = selectLatestIsoTimestamp(context.updatedAt, context.startedAt);
|
|
1195
|
+
return !contextUpdatedAt || compareIsoTimestamp(candidateStartedAt, contextUpdatedAt) > 0;
|
|
1196
|
+
}
|
|
1197
|
+
return compareIsoTimestamp(candidateStartedAt, contextStartedAt) > 0;
|
|
1198
|
+
}
|
|
1199
|
+
function selectProviderLinearWorkerContextChronologyTimestamp(context) {
|
|
1200
|
+
return selectLatestIsoTimestamp(context.startedAt, parseProviderLinearWorkerRunIdTimestamp(context.runId), parseProviderLinearWorkerRunPathTimestamp(context.manifestPath ?? context.runDir ?? null));
|
|
1201
|
+
}
|
|
1202
|
+
function parseProviderLinearWorkerRunPathTimestamp(pathValue) {
|
|
1203
|
+
if (!pathValue) {
|
|
1204
|
+
return null;
|
|
1205
|
+
}
|
|
1206
|
+
const normalized = pathValue.replace(/\\/g, '/');
|
|
1207
|
+
const segments = normalized.split('/').filter(Boolean);
|
|
1208
|
+
const manifestIndex = segments.lastIndexOf('manifest.json');
|
|
1209
|
+
if (manifestIndex > 0) {
|
|
1210
|
+
return parseProviderLinearWorkerRunIdTimestamp(segments[manifestIndex - 1]);
|
|
1211
|
+
}
|
|
1212
|
+
return parseProviderLinearWorkerRunIdTimestamp(segments[segments.length - 1]);
|
|
1213
|
+
}
|
|
1214
|
+
function parseProviderLinearWorkerRunIdTimestamp(runId) {
|
|
1215
|
+
if (!runId) {
|
|
1216
|
+
return null;
|
|
1217
|
+
}
|
|
1218
|
+
const match = runId.match(/\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z/);
|
|
1219
|
+
if (!match) {
|
|
1220
|
+
return null;
|
|
1221
|
+
}
|
|
1222
|
+
const iso = match[0].replace(/^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z$/, '$1T$2:$3:$4.$5Z');
|
|
1223
|
+
return Number.isFinite(Date.parse(iso)) ? iso : null;
|
|
1224
|
+
}
|
|
1225
|
+
function findNewerRunBoundProviderLinearWorkerClaimForContext(providerIntakeState, context) {
|
|
1226
|
+
return providerIntakeState.claims
|
|
1227
|
+
.filter((claim) => claim.provider === 'linear' &&
|
|
1228
|
+
isProviderLinearWorkerRunBoundClaimAuthoritative(claim) &&
|
|
1229
|
+
providerLinearWorkerClaimHasRunIdentity(claim) &&
|
|
1230
|
+
!providerLinearWorkerClaimRunIdentityMatchesContext(claim, context) &&
|
|
1231
|
+
providerLinearWorkerClaimIssueIdentityMatchesContext(claim, context) &&
|
|
1232
|
+
isProviderLinearWorkerRunBoundClaimNewerThanContext(claim, context))
|
|
1233
|
+
.sort(compareProviderLinearWorkerRunBoundClaimDesc)[0] ?? null;
|
|
1234
|
+
}
|
|
1235
|
+
function isProviderLinearWorkerRunBoundClaimNewerThanContext(claim, context) {
|
|
1236
|
+
const claimRunEvidenceAt = selectProviderLinearWorkerClaimRunEvidenceTimestamp(claim);
|
|
1237
|
+
const contextRunEvidenceAt = selectProviderLinearWorkerContextChronologyTimestamp(context);
|
|
1238
|
+
if (claimRunEvidenceAt && contextRunEvidenceAt) {
|
|
1239
|
+
return compareIsoTimestamp(claimRunEvidenceAt, contextRunEvidenceAt) > 0;
|
|
1240
|
+
}
|
|
1241
|
+
if (claimRunEvidenceAt && !contextRunEvidenceAt) {
|
|
1242
|
+
return true;
|
|
1243
|
+
}
|
|
1244
|
+
return compareIsoTimestamp(claim.updated_at, context.updatedAt) > 0;
|
|
1245
|
+
}
|
|
1246
|
+
function compareProviderLinearWorkerRunBoundClaimDesc(left, right) {
|
|
1247
|
+
const chronologyComparison = compareIsoTimestamp(selectProviderLinearWorkerClaimRunEvidenceTimestamp(right), selectProviderLinearWorkerClaimRunEvidenceTimestamp(left));
|
|
1248
|
+
return chronologyComparison !== 0
|
|
1249
|
+
? chronologyComparison
|
|
1250
|
+
: compareIsoTimestamp(right.updated_at, left.updated_at);
|
|
1251
|
+
}
|
|
1252
|
+
function selectProviderLinearWorkerClaimRunEvidenceTimestamp(claim) {
|
|
1253
|
+
return selectLatestIsoTimestamp(parseProviderLinearWorkerRunIdTimestamp(claim.run_id), parseProviderLinearWorkerRunPathTimestamp(claim.run_manifest_path));
|
|
1254
|
+
}
|
|
1255
|
+
function isProviderLinearWorkerRunBoundClaimAuthoritative(claim) {
|
|
1256
|
+
return (isActiveProviderLinearWorkerClaimState(claim.state) ||
|
|
1257
|
+
(claim.state === 'released' && isLiveRehydrateProviderLinearWorkerReleasedClaim(claim)));
|
|
1258
|
+
}
|
|
1259
|
+
function providerLinearWorkerClaimHasRunIdentity(claim) {
|
|
1260
|
+
return Boolean(claim.run_id || claim.run_manifest_path);
|
|
1261
|
+
}
|
|
1262
|
+
function providerLinearWorkerClaimRunIdentityMatchesContext(claim, context) {
|
|
1263
|
+
const manifestPath = context.manifestPath ?? null;
|
|
1264
|
+
return Boolean((claim.run_id && claim.run_id === context.runId) ||
|
|
1265
|
+
(claim.run_manifest_path && manifestPath && claim.run_manifest_path === manifestPath));
|
|
1266
|
+
}
|
|
1267
|
+
function providerLinearWorkerClaimIssueIdentityMatchesContext(claim, context) {
|
|
1268
|
+
return Boolean((claim.issue_id && claim.issue_id === context.issueId) ||
|
|
1269
|
+
(claim.issue_identifier && claim.issue_identifier === context.issueIdentifier) ||
|
|
1270
|
+
(claim.task_id && claim.task_id === context.taskId));
|
|
1271
|
+
}
|
|
1272
|
+
function providerLinearWorkerContextsShareIssueIdentity(left, right) {
|
|
1273
|
+
return Boolean((left.issueId && right.issueId && left.issueId === right.issueId) ||
|
|
1274
|
+
(left.issueIdentifier && right.issueIdentifier && left.issueIdentifier === right.issueIdentifier) ||
|
|
1275
|
+
(left.taskId && right.taskId && left.taskId === right.taskId));
|
|
1276
|
+
}
|
|
1277
|
+
function selectLatestIsoTimestamp(...values) {
|
|
1278
|
+
return values
|
|
1279
|
+
.filter((value) => Boolean(value))
|
|
1280
|
+
.sort((left, right) => compareIsoTimestamp(right, left))[0] ?? null;
|
|
1281
|
+
}
|
|
1282
|
+
async function readDirectoryNames(path) {
|
|
1283
|
+
try {
|
|
1284
|
+
const entries = await readdir(path, { withFileTypes: true });
|
|
1285
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
1286
|
+
}
|
|
1287
|
+
catch {
|
|
1288
|
+
return [];
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
async function readJsonFile(path) {
|
|
1292
|
+
try {
|
|
1293
|
+
const raw = await readFile(path, 'utf8');
|
|
1294
|
+
return JSON.parse(raw);
|
|
1295
|
+
}
|
|
1296
|
+
catch {
|
|
1297
|
+
return null;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
async function readProviderLinearWorkerProofForProjection(runDir) {
|
|
1301
|
+
const proof = await readJsonFile(join(runDir, PROVIDER_LINEAR_WORKER_PROOF_FILENAME));
|
|
1302
|
+
const refreshPlan = await resolveProviderLinearWorkerProjectionProofRefreshPlan(runDir, proof);
|
|
1303
|
+
if (!refreshPlan) {
|
|
1304
|
+
return proof;
|
|
1305
|
+
}
|
|
1306
|
+
return ((await refreshProviderLinearWorkerProofSnapshot(runDir, null, undefined, undefined, process.env, {
|
|
1307
|
+
updatedAtComparisonScope: refreshPlan.updatedAtComparisonScope,
|
|
1308
|
+
skipSessionLogHydration: refreshPlan.skipSessionLogHydration
|
|
1309
|
+
}).catch(() => proof)) ?? proof);
|
|
1310
|
+
}
|
|
1311
|
+
async function resolveProviderLinearWorkerProjectionProofRefreshPlan(runDir, proof) {
|
|
1312
|
+
if (!proof) {
|
|
1313
|
+
return null;
|
|
1314
|
+
}
|
|
1315
|
+
const hasRetiredResidue = hasProviderLinearWorkerProjectionRetiredChildLaneResidue(proof) ||
|
|
1316
|
+
(await hasProviderLinearWorkerProjectionRetiredChildLaneResidueInLedger(runDir));
|
|
1317
|
+
const telemetryGap = hasProviderLinearWorkerProjectionTelemetryGap(proof);
|
|
1318
|
+
const canSkipSessionLogHydration = canSkipProviderLinearWorkerProjectionSessionLogHydration(proof, telemetryGap);
|
|
1319
|
+
if (!isProviderLinearWorkerProjectionRefreshEligible(proof)) {
|
|
1320
|
+
return hasRetiredResidue
|
|
1321
|
+
? {
|
|
1322
|
+
updatedAtComparisonScope: 'full',
|
|
1323
|
+
skipSessionLogHydration: canSkipSessionLogHydration
|
|
1324
|
+
}
|
|
1325
|
+
: null;
|
|
1326
|
+
}
|
|
1327
|
+
if (hasRetiredResidue) {
|
|
1328
|
+
return {
|
|
1329
|
+
updatedAtComparisonScope: 'full',
|
|
1330
|
+
skipSessionLogHydration: canSkipSessionLogHydration
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
if (hasProviderLinearWorkerProjectionReservationPlaceholder(proof)) {
|
|
1334
|
+
return {
|
|
1335
|
+
updatedAtComparisonScope: 'full',
|
|
1336
|
+
skipSessionLogHydration: canSkipSessionLogHydration
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
if (hasProviderLinearWorkerProjectionActivePendingChildLane(proof)) {
|
|
1340
|
+
return {
|
|
1341
|
+
updatedAtComparisonScope: 'full',
|
|
1342
|
+
skipSessionLogHydration: canSkipSessionLogHydration
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
if (await hasProviderLinearWorkerProjectionActivePendingChildLaneInLedger(runDir)) {
|
|
1346
|
+
return {
|
|
1347
|
+
updatedAtComparisonScope: 'full',
|
|
1348
|
+
skipSessionLogHydration: canSkipSessionLogHydration
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
return telemetryGap
|
|
1352
|
+
? {
|
|
1353
|
+
updatedAtComparisonScope: 'telemetry',
|
|
1354
|
+
skipSessionLogHydration: false
|
|
1355
|
+
}
|
|
1356
|
+
: null;
|
|
1357
|
+
}
|
|
1358
|
+
function isProviderLinearWorkerProjectionRefreshEligible(proof) {
|
|
1359
|
+
return (proof.owner_status === 'in_progress' &&
|
|
1360
|
+
(proof.owner_phase === 'turn_running' || proof.owner_phase === 'turn_completed'));
|
|
1361
|
+
}
|
|
1362
|
+
function hasProviderLinearWorkerProjectionReservationPlaceholder(proof) {
|
|
1363
|
+
return hasProviderLinearWorkerProjectionReservationPlaceholderInRecords(proof.child_lanes);
|
|
1364
|
+
}
|
|
1365
|
+
function hasProviderLinearWorkerProjectionActivePendingChildLane(proof) {
|
|
1366
|
+
return hasProviderLinearWorkerProjectionActivePendingChildLaneInRecords(proof.child_lanes);
|
|
1367
|
+
}
|
|
1368
|
+
function hasProviderLinearWorkerProjectionRetiredChildLaneResidue(proof) {
|
|
1369
|
+
return hasProviderLinearWorkerProjectionRetiredChildLaneResidueInRecords(proof.child_lanes);
|
|
1370
|
+
}
|
|
1371
|
+
function hasProviderLinearWorkerProjectionTelemetryGap(proof) {
|
|
1372
|
+
const tokens = proof.tokens ?? null;
|
|
1373
|
+
const hasTokens = tokens?.input_tokens != null ||
|
|
1374
|
+
tokens?.output_tokens != null ||
|
|
1375
|
+
tokens?.total_tokens != null ||
|
|
1376
|
+
tokens?.reasoning_output_tokens != null;
|
|
1377
|
+
return (!proof.latest_turn_id ||
|
|
1378
|
+
!proof.latest_session_id ||
|
|
1379
|
+
!hasTokens ||
|
|
1380
|
+
proof.rate_limits == null ||
|
|
1381
|
+
hasProviderLinearWorkerProjectionSessionLogHydrationGap(proof) ||
|
|
1382
|
+
hasProviderLinearWorkerProjectionAppServerSupervisionGap(proof));
|
|
1383
|
+
}
|
|
1384
|
+
function hasProviderLinearWorkerProjectionAppServerSupervisionGap(proof) {
|
|
1385
|
+
const selectedRuntimeMode = proof.runtime?.selected_mode ?? proof.auth_provenance?.runtime_mode ?? null;
|
|
1386
|
+
const requestedRuntimeMode = proof.runtime?.requested_mode ?? null;
|
|
1387
|
+
const fallback = proof.runtime?.fallback ?? null;
|
|
1388
|
+
if (selectedRuntimeMode !== 'appserver' &&
|
|
1389
|
+
requestedRuntimeMode !== 'appserver' &&
|
|
1390
|
+
fallback?.from_mode !== 'appserver' &&
|
|
1391
|
+
fallback?.to_mode !== 'appserver') {
|
|
1392
|
+
return false;
|
|
1393
|
+
}
|
|
1394
|
+
const supervision = proof.appserver_supervision ?? null;
|
|
1395
|
+
if (!supervision) {
|
|
1396
|
+
return true;
|
|
1397
|
+
}
|
|
1398
|
+
const expectedSessionLogTruthRetained = selectedRuntimeMode === 'appserver';
|
|
1399
|
+
return (supervision.selected_runtime?.selected_mode !== selectedRuntimeMode ||
|
|
1400
|
+
supervision.selected_runtime?.requested_mode !== requestedRuntimeMode ||
|
|
1401
|
+
supervision.thread_id !== proof.thread_id ||
|
|
1402
|
+
supervision.latest_turn_id !== proof.latest_turn_id ||
|
|
1403
|
+
supervision.latest_session_id !== proof.latest_session_id ||
|
|
1404
|
+
supervision.session_log_thread_id !== (proof.session_log_thread_id ?? null) ||
|
|
1405
|
+
supervision.session_log_turn_id !== (proof.session_log_turn_id ?? null) ||
|
|
1406
|
+
supervision.session_log_session_id !== (proof.session_log_session_id ?? null) ||
|
|
1407
|
+
supervision.turn_persistence_status == null ||
|
|
1408
|
+
supervision.pagination_status == null ||
|
|
1409
|
+
supervision.resume_status == null ||
|
|
1410
|
+
supervision.fork_status == null ||
|
|
1411
|
+
supervision.jsonl_truth_retained !== true ||
|
|
1412
|
+
supervision.session_log_truth_retained !== expectedSessionLogTruthRetained);
|
|
1413
|
+
}
|
|
1414
|
+
function hasProviderLinearWorkerProjectionSessionLogHydrationGap(proof) {
|
|
1415
|
+
const runtimeMode = proof.runtime?.selected_mode ?? proof.auth_provenance?.runtime_mode ?? null;
|
|
1416
|
+
if (runtimeMode !== 'appserver') {
|
|
1417
|
+
return false;
|
|
1418
|
+
}
|
|
1419
|
+
if (!proof.thread_id || !proof.latest_turn_id || !proof.latest_session_id) {
|
|
1420
|
+
return false;
|
|
1421
|
+
}
|
|
1422
|
+
return (proof.session_log_thread_id !== proof.thread_id ||
|
|
1423
|
+
proof.session_log_turn_id !== proof.latest_turn_id ||
|
|
1424
|
+
proof.session_log_session_id !== proof.latest_session_id);
|
|
1425
|
+
}
|
|
1426
|
+
function canSkipProviderLinearWorkerProjectionSessionLogHydration(proof, telemetryGap) {
|
|
1427
|
+
if (proof.owner_phase !== 'turn_completed') {
|
|
1428
|
+
return false;
|
|
1429
|
+
}
|
|
1430
|
+
const currentTurnActivity = proof.current_turn_activity ?? null;
|
|
1431
|
+
return !(telemetryGap ||
|
|
1432
|
+
!proof.last_event_at ||
|
|
1433
|
+
(!proof.last_event && !proof.last_message) ||
|
|
1434
|
+
currentTurnActivity == null ||
|
|
1435
|
+
currentTurnActivity.recorded_at == null ||
|
|
1436
|
+
currentTurnActivity.turn_id !== proof.latest_turn_id ||
|
|
1437
|
+
currentTurnActivity.session_id !== proof.latest_session_id ||
|
|
1438
|
+
proof.auth_provenance == null);
|
|
1439
|
+
}
|
|
1440
|
+
async function hasProviderLinearWorkerProjectionActivePendingChildLaneInLedger(runDir) {
|
|
1441
|
+
const records = await readJsonFile(join(runDir, PROVIDER_LINEAR_WORKER_CHILD_LANES_FILENAME));
|
|
1442
|
+
return hasProviderLinearWorkerProjectionActivePendingChildLaneInRecords(records);
|
|
1443
|
+
}
|
|
1444
|
+
async function hasProviderLinearWorkerProjectionRetiredChildLaneResidueInLedger(runDir) {
|
|
1445
|
+
const records = await readJsonFile(join(runDir, PROVIDER_LINEAR_WORKER_CHILD_LANES_FILENAME));
|
|
1446
|
+
return hasProviderLinearWorkerProjectionRetiredChildLaneResidueInRecords(records);
|
|
1447
|
+
}
|
|
1448
|
+
function hasProviderLinearWorkerProjectionReservationPlaceholderInRecords(records) {
|
|
1449
|
+
if (!Array.isArray(records)) {
|
|
1450
|
+
return false;
|
|
1451
|
+
}
|
|
1452
|
+
return records.some((childLane) => {
|
|
1453
|
+
if (!isRecord(childLane)) {
|
|
1454
|
+
return false;
|
|
1455
|
+
}
|
|
1456
|
+
const runId = readStringValue(childLane, 'run_id');
|
|
1457
|
+
const summary = readStringValue(childLane, 'summary');
|
|
1458
|
+
const status = readStringValue(childLane, 'status');
|
|
1459
|
+
const pipelineId = readStringValue(childLane, 'pipeline_id');
|
|
1460
|
+
const decision = readStringValue(childLane, 'decision');
|
|
1461
|
+
return (pipelineId === PROVIDER_LINEAR_CHILD_LANE_PIPELINE_ID &&
|
|
1462
|
+
decision === 'pending' &&
|
|
1463
|
+
(status === 'launching' ||
|
|
1464
|
+
Boolean(runId?.startsWith('launching-')) ||
|
|
1465
|
+
summary === PROVIDER_LINEAR_CHILD_LANE_RESERVED_SUMMARY));
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
function hasProviderLinearWorkerProjectionActivePendingChildLaneInRecords(records) {
|
|
1469
|
+
if (!Array.isArray(records)) {
|
|
1470
|
+
return false;
|
|
1471
|
+
}
|
|
1472
|
+
return records.some((childLane) => {
|
|
1473
|
+
if (!isRecord(childLane)) {
|
|
1474
|
+
return false;
|
|
1475
|
+
}
|
|
1476
|
+
const status = readStringValue(childLane, 'status');
|
|
1477
|
+
return (readStringValue(childLane, 'pipeline_id') === PROVIDER_LINEAR_CHILD_LANE_PIPELINE_ID &&
|
|
1478
|
+
readStringValue(childLane, 'decision') === 'pending' &&
|
|
1479
|
+
!isProviderLinearWorkerProjectionTerminalChildLaneStatus(status ?? null));
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
function hasProviderLinearWorkerProjectionRetiredChildLaneResidueInRecords(records) {
|
|
1483
|
+
if (!Array.isArray(records)) {
|
|
1484
|
+
return false;
|
|
1485
|
+
}
|
|
1486
|
+
return records.some((childLane) => {
|
|
1487
|
+
if (!isRecord(childLane)) {
|
|
1488
|
+
return false;
|
|
1489
|
+
}
|
|
1490
|
+
const status = readStringValue(childLane, 'status');
|
|
1491
|
+
const summary = readStringValue(childLane, 'summary');
|
|
1492
|
+
const decision = readStringValue(childLane, 'decision');
|
|
1493
|
+
const inFlightAction = readStringValue(childLane, 'in_flight_action');
|
|
1494
|
+
const hasActiveLookingStatus = status === 'launching' ||
|
|
1495
|
+
status === 'in_progress' ||
|
|
1496
|
+
status === 'running' ||
|
|
1497
|
+
status === 'queued';
|
|
1498
|
+
return (readStringValue(childLane, 'pipeline_id') === PROVIDER_LINEAR_CHILD_LANE_PIPELINE_ID &&
|
|
1499
|
+
(decision === 'invalidated' || decision === 'rejected') &&
|
|
1500
|
+
(hasActiveLookingStatus ||
|
|
1501
|
+
Boolean(inFlightAction) ||
|
|
1502
|
+
isProviderLinearWorkerProjectionActiveLookingChildLaneSummary(summary)));
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
function isProviderLinearWorkerProjectionActiveLookingChildLaneSummary(summary) {
|
|
1506
|
+
if (!summary) {
|
|
1507
|
+
return false;
|
|
1508
|
+
}
|
|
1509
|
+
const normalized = summary.toLowerCase();
|
|
1510
|
+
return (summary === PROVIDER_LINEAR_CHILD_LANE_RESERVED_SUMMARY ||
|
|
1511
|
+
normalized.includes(' is running') ||
|
|
1512
|
+
normalized.includes(' is queued') ||
|
|
1513
|
+
normalized.includes(' status is in_progress') ||
|
|
1514
|
+
normalized.includes(' status is running') ||
|
|
1515
|
+
normalized.includes(' status is queued') ||
|
|
1516
|
+
normalized.includes(' status is launching') ||
|
|
1517
|
+
normalized.includes('reserved before child run startup'));
|
|
1518
|
+
}
|
|
1519
|
+
function isProviderLinearWorkerProjectionTerminalChildLaneStatus(status) {
|
|
1520
|
+
return (status === 'succeeded' ||
|
|
1521
|
+
status === 'failed' ||
|
|
1522
|
+
status === 'completed' ||
|
|
1523
|
+
status === 'canceled' ||
|
|
1524
|
+
status === 'cancelled' ||
|
|
1525
|
+
status === 'invalidated' ||
|
|
1526
|
+
status === 'rejected');
|
|
1527
|
+
}
|
|
1528
|
+
function isManifestRetryFallbackCandidate(manifestRecord) {
|
|
1529
|
+
if (readStringValue(manifestRecord, 'issue_provider', 'issueProvider') !== 'linear') {
|
|
1530
|
+
return false;
|
|
1531
|
+
}
|
|
1532
|
+
const status = readStringValue(manifestRecord, 'status');
|
|
1533
|
+
return status === 'failed' || status === 'cancelled' || status === 'canceled';
|
|
1534
|
+
}
|
|
1535
|
+
function findMatchingProviderIntakeClaim(state, snapshot, providerLinearWorkerProof = null) {
|
|
1536
|
+
if (!state || !snapshot) {
|
|
1537
|
+
return null;
|
|
1538
|
+
}
|
|
1539
|
+
const claimMatchSource = buildSelectedRunProviderClaimMatchSource(snapshot, providerLinearWorkerProof);
|
|
1540
|
+
const issueScopedClaim = findIssueScopedProviderIntakeClaim(state, claimMatchSource);
|
|
1541
|
+
if (issueScopedClaim) {
|
|
1542
|
+
return providerIntakeClaimCanFallbackByIssue(issueScopedClaim, claimMatchSource) ? issueScopedClaim : null;
|
|
1543
|
+
}
|
|
1544
|
+
const matchedClaims = state.claims
|
|
1545
|
+
.filter((claim) => providerIntakeClaimMatchesSelectedRun(claim, claimMatchSource))
|
|
1546
|
+
.sort((left, right) => compareProviderIntakeClaimSpecificity(right, left, claimMatchSource));
|
|
1547
|
+
return matchedClaims[0] ?? null;
|
|
1548
|
+
}
|
|
1549
|
+
function findIssueScopedProviderIntakeClaim(state, snapshot) {
|
|
1550
|
+
if (snapshot.issueId) {
|
|
1551
|
+
const byIssueId = readProviderIntakeClaim(state, buildProviderIssueKey('linear', snapshot.issueId));
|
|
1552
|
+
if (byIssueId) {
|
|
1553
|
+
return byIssueId;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
return state.claims.find((claim) => claim.issue_identifier === snapshot.issueIdentifier) ?? null;
|
|
1557
|
+
}
|
|
1558
|
+
function providerIntakeClaimMatchesSelectedRun(claim, snapshot) {
|
|
1559
|
+
if (claim.run_manifest_path && claim.run_manifest_path === snapshot.manifestPath) {
|
|
1560
|
+
return true;
|
|
1561
|
+
}
|
|
1562
|
+
if (!providerIntakeClaimMatchesIssueIdentity(claim, snapshot)) {
|
|
1563
|
+
return providerIntakeClaimMatchesSyntheticFallbackTaskBinding(claim, snapshot);
|
|
1564
|
+
}
|
|
1565
|
+
if (claim.run_id && snapshot.runId) {
|
|
1566
|
+
if (claim.task_id && snapshot.taskId && claim.task_id !== snapshot.taskId) {
|
|
1567
|
+
return false;
|
|
1568
|
+
}
|
|
1569
|
+
return claim.run_id === snapshot.runId;
|
|
1570
|
+
}
|
|
1571
|
+
if (claim.task_id && snapshot.taskId) {
|
|
1572
|
+
return claim.task_id === snapshot.taskId;
|
|
1573
|
+
}
|
|
1574
|
+
return false;
|
|
1575
|
+
}
|
|
1576
|
+
function compareProviderIntakeClaimSpecificity(left, right, snapshot) {
|
|
1577
|
+
const leftPriority = scoreProviderIntakeClaimSpecificity(left, snapshot);
|
|
1578
|
+
const rightPriority = scoreProviderIntakeClaimSpecificity(right, snapshot);
|
|
1579
|
+
if (leftPriority !== rightPriority) {
|
|
1580
|
+
return leftPriority - rightPriority;
|
|
1581
|
+
}
|
|
1582
|
+
const leftTaskLength = left.task_id?.length ?? 0;
|
|
1583
|
+
const rightTaskLength = right.task_id?.length ?? 0;
|
|
1584
|
+
if (leftTaskLength !== rightTaskLength) {
|
|
1585
|
+
return leftTaskLength - rightTaskLength;
|
|
1586
|
+
}
|
|
1587
|
+
return (left.issue_identifier ?? '').localeCompare(right.issue_identifier ?? '');
|
|
1588
|
+
}
|
|
1589
|
+
function scoreProviderIntakeClaimSpecificity(claim, snapshot) {
|
|
1590
|
+
if (claim.run_manifest_path && claim.run_manifest_path === snapshot.manifestPath) {
|
|
1591
|
+
return 4;
|
|
1592
|
+
}
|
|
1593
|
+
if (claim.run_id && snapshot.runId && claim.run_id === snapshot.runId) {
|
|
1594
|
+
return 3;
|
|
1595
|
+
}
|
|
1596
|
+
if (claim.task_id && snapshot.taskId && claim.task_id === snapshot.taskId) {
|
|
1597
|
+
return 2;
|
|
1598
|
+
}
|
|
1599
|
+
if (providerIntakeClaimMatchesSyntheticFallbackTaskBinding(claim, snapshot)) {
|
|
1600
|
+
return 1;
|
|
1601
|
+
}
|
|
1602
|
+
return 0;
|
|
1603
|
+
}
|
|
1604
|
+
function providerIntakeClaimCanFallbackByIssue(claim, snapshot) {
|
|
1605
|
+
return (providerIntakeClaimMatchesSelectedRun(claim, snapshot) ||
|
|
1606
|
+
(!claim.run_manifest_path && !claim.run_id && !claim.task_id));
|
|
1607
|
+
}
|
|
1608
|
+
function providerIntakeClaimMatchesIssueIdentity(claim, snapshot) {
|
|
1609
|
+
return ((claim.issue_id != null && snapshot.issueId != null && claim.issue_id === snapshot.issueId) ||
|
|
1610
|
+
claim.issue_identifier === snapshot.issueIdentifier);
|
|
1611
|
+
}
|
|
1612
|
+
function providerIntakeClaimMatchesSyntheticFallbackTaskBinding(claim, snapshot) {
|
|
1613
|
+
if (!claim.task_id || !snapshot.taskId) {
|
|
1614
|
+
return false;
|
|
1615
|
+
}
|
|
1616
|
+
if (!hasProviderLinearClaimBindingProvenance(snapshot, snapshot.providerLinearWorkerProof)) {
|
|
1617
|
+
return false;
|
|
1618
|
+
}
|
|
1619
|
+
if (claim.task_id !== buildProviderFallbackTaskId({ id: claim.issue_id })) {
|
|
1620
|
+
return false;
|
|
1621
|
+
}
|
|
1622
|
+
if (snapshot.taskId === claim.task_id) {
|
|
1623
|
+
return !hasAuthoritativeProjectionIssueIdentity(snapshot);
|
|
1624
|
+
}
|
|
1625
|
+
const pipelineId = readStringValue(snapshot.manifestRecord, 'pipeline_id', 'pipelineId') ?? null;
|
|
1626
|
+
return (matchesSyntheticProviderChildTaskId(claim.task_id, snapshot.taskId, pipelineId) &&
|
|
1627
|
+
!hasAuthoritativeProjectionIssueIdentity(snapshot));
|
|
1628
|
+
}
|
|
1629
|
+
function isProviderLinearChildPipelineId(pipelineId) {
|
|
1630
|
+
return pipelineId !== null && PROVIDER_LINEAR_CHILD_PIPELINE_IDS.has(pipelineId);
|
|
1631
|
+
}
|
|
1632
|
+
function matchesSyntheticProviderChildTaskId(claimTaskId, snapshotTaskId, pipelineId) {
|
|
1633
|
+
if (!isProviderLinearChildPipelineId(pipelineId)) {
|
|
1634
|
+
return false;
|
|
1635
|
+
}
|
|
1636
|
+
if (pipelineId === 'provider-linear-child-lane') {
|
|
1637
|
+
return snapshotTaskId.startsWith(`${claimTaskId}-`);
|
|
1638
|
+
}
|
|
1639
|
+
return snapshotTaskId === `${claimTaskId}-${pipelineId}`;
|
|
1640
|
+
}
|
|
1641
|
+
function hasProviderLinearClaimBindingProvenance(snapshot, providerLinearWorkerProof) {
|
|
1642
|
+
if (snapshot.issueProvider !== null && snapshot.issueProvider !== 'linear') {
|
|
1643
|
+
return false;
|
|
1644
|
+
}
|
|
1645
|
+
const pipelineId = readStringValue(snapshot.manifestRecord, 'pipeline_id', 'pipelineId') ?? null;
|
|
1646
|
+
const pipelineTitle = readStringValue(snapshot.manifestRecord, 'pipeline_title', 'pipelineTitle') ?? null;
|
|
1647
|
+
return (pipelineId === PROVIDER_LINEAR_WORKER_PIPELINE_ID ||
|
|
1648
|
+
pipelineTitle === PROVIDER_LINEAR_WORKER_PIPELINE_TITLE ||
|
|
1649
|
+
providerLinearWorkerProof != null ||
|
|
1650
|
+
(snapshot.issueProvider === 'linear' && isProviderLinearChildPipelineId(pipelineId)));
|
|
1651
|
+
}
|
|
1652
|
+
function buildSelectedRunProviderClaimMatchSource(snapshot, providerLinearWorkerProof) {
|
|
1653
|
+
return {
|
|
1654
|
+
issueId: snapshot.issueId,
|
|
1655
|
+
issueIdentifier: snapshot.issueIdentifier,
|
|
1656
|
+
issueProvider: snapshot.issueProvider,
|
|
1657
|
+
manifestPath: snapshot.manifestPath,
|
|
1658
|
+
manifestRecord: snapshot.manifestRecord,
|
|
1659
|
+
runId: snapshot.runId,
|
|
1660
|
+
taskId: snapshot.taskId,
|
|
1661
|
+
providerLinearWorkerProof
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
function hasAuthoritativeProjectionIssueIdentity(snapshot) {
|
|
1665
|
+
if (snapshot.issueIdentifier && !isProjectionFallbackIdentityValue(snapshot.issueIdentifier, snapshot)) {
|
|
1666
|
+
return true;
|
|
1667
|
+
}
|
|
1668
|
+
if (snapshot.issueId && !isProjectionFallbackIdentityValue(snapshot.issueId, snapshot)) {
|
|
1669
|
+
return true;
|
|
1670
|
+
}
|
|
1671
|
+
return false;
|
|
1672
|
+
}
|
|
1673
|
+
function buildProviderRetryState(claim) {
|
|
1674
|
+
if (!claim) {
|
|
1675
|
+
return null;
|
|
1676
|
+
}
|
|
1677
|
+
const active = claim.retry_queued === true;
|
|
1678
|
+
const attempt = claim.retry_attempt ?? null;
|
|
1679
|
+
const dueAt = claim.retry_due_at ?? null;
|
|
1680
|
+
const error = claim.retry_error ?? null;
|
|
1681
|
+
if (!active && attempt === null && dueAt === null && error === null) {
|
|
1682
|
+
return null;
|
|
1683
|
+
}
|
|
1684
|
+
return {
|
|
1685
|
+
active,
|
|
1686
|
+
attempt,
|
|
1687
|
+
due_at: dueAt,
|
|
1688
|
+
error
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
async function buildProviderRetryContextFromClaim(context, claim) {
|
|
1692
|
+
const retryLatestEvent = claim.reason !== null
|
|
1693
|
+
? {
|
|
1694
|
+
at: claim.updated_at,
|
|
1695
|
+
event: claim.state,
|
|
1696
|
+
message: claim.reason,
|
|
1697
|
+
requestedBy: null,
|
|
1698
|
+
reason: claim.reason
|
|
1699
|
+
}
|
|
1700
|
+
: null;
|
|
1701
|
+
const snapshot = claim.run_manifest_path
|
|
1702
|
+
? await readManifestSnapshotForPath(claim.run_manifest_path, claim.run_id ?? null)
|
|
1703
|
+
: null;
|
|
1704
|
+
const controlWorkspacePath = await resolveControlWorkspacePath(context);
|
|
1705
|
+
if (!snapshot) {
|
|
1706
|
+
return {
|
|
1707
|
+
issueProvider: claim.provider,
|
|
1708
|
+
issueIdentifier: claim.issue_identifier,
|
|
1709
|
+
issueId: claim.issue_id,
|
|
1710
|
+
taskId: claim.task_id,
|
|
1711
|
+
runId: claim.run_id,
|
|
1712
|
+
lookupAliases: buildProjectionLookupAliases({
|
|
1713
|
+
issueIdentifier: claim.issue_identifier,
|
|
1714
|
+
issueId: claim.issue_id,
|
|
1715
|
+
taskId: claim.task_id,
|
|
1716
|
+
runId: claim.run_id
|
|
1717
|
+
}),
|
|
1718
|
+
rawStatus: claim.state,
|
|
1719
|
+
displayStatus: 'retrying',
|
|
1720
|
+
statusReason: claim.reason,
|
|
1721
|
+
startedAt: claim.accepted_at,
|
|
1722
|
+
updatedAt: claim.updated_at,
|
|
1723
|
+
completedAt: null,
|
|
1724
|
+
summary: claim.reason,
|
|
1725
|
+
lastError: claim.retry_error ?? null,
|
|
1726
|
+
latestAction: null,
|
|
1727
|
+
latestEvent: retryLatestEvent,
|
|
1728
|
+
workspacePath: resolveRetryWorkspacePath(claim, controlWorkspacePath),
|
|
1729
|
+
pipelineTitle: null,
|
|
1730
|
+
stages: [],
|
|
1731
|
+
approvalsTotal: 0,
|
|
1732
|
+
manifestPath: claim.run_manifest_path,
|
|
1733
|
+
runDir: claim.run_manifest_path ? dirname(claim.run_manifest_path) : null,
|
|
1734
|
+
questionSummary: {
|
|
1735
|
+
queuedCount: 0,
|
|
1736
|
+
latestQuestion: null
|
|
1737
|
+
},
|
|
1738
|
+
tracked: null,
|
|
1739
|
+
compatibilityState: claim.issue_state,
|
|
1740
|
+
providerLinearWorkerProof: null,
|
|
1741
|
+
providerDebugSnapshot: buildProviderIssueDebugSnapshot({
|
|
1742
|
+
claim,
|
|
1743
|
+
rehydrated_at: context.providerIntakeState?.rehydrated_at ?? null
|
|
1744
|
+
}),
|
|
1745
|
+
providerRetryState: buildProviderRetryState(claim)
|
|
1746
|
+
};
|
|
1747
|
+
}
|
|
1748
|
+
const parts = await resolveProjectionContextParts(context, snapshot);
|
|
1749
|
+
const base = buildProjectionContextFromParts(snapshot, parts, resolveRunsRootFromRunDir(context.paths.runDir), controlWorkspacePath, claim, context.providerIntakeState ?? null) ??
|
|
1750
|
+
null;
|
|
1751
|
+
if (!base) {
|
|
1752
|
+
return {
|
|
1753
|
+
issueProvider: claim.provider,
|
|
1754
|
+
issueIdentifier: claim.issue_identifier,
|
|
1755
|
+
issueId: claim.issue_id,
|
|
1756
|
+
taskId: claim.task_id,
|
|
1757
|
+
runId: claim.run_id,
|
|
1758
|
+
lookupAliases: buildProjectionLookupAliases({
|
|
1759
|
+
issueIdentifier: claim.issue_identifier,
|
|
1760
|
+
issueId: claim.issue_id,
|
|
1761
|
+
taskId: claim.task_id,
|
|
1762
|
+
runId: claim.run_id
|
|
1763
|
+
}),
|
|
1764
|
+
rawStatus: claim.state,
|
|
1765
|
+
displayStatus: 'retrying',
|
|
1766
|
+
statusReason: claim.reason,
|
|
1767
|
+
startedAt: claim.accepted_at,
|
|
1768
|
+
updatedAt: claim.updated_at,
|
|
1769
|
+
completedAt: null,
|
|
1770
|
+
summary: claim.reason,
|
|
1771
|
+
lastError: claim.retry_error ?? null,
|
|
1772
|
+
latestAction: null,
|
|
1773
|
+
latestEvent: null,
|
|
1774
|
+
workspacePath: resolveRetryWorkspacePath(claim, controlWorkspacePath),
|
|
1775
|
+
pipelineTitle: null,
|
|
1776
|
+
stages: [],
|
|
1777
|
+
approvalsTotal: 0,
|
|
1778
|
+
manifestPath: claim.run_manifest_path,
|
|
1779
|
+
runDir: claim.run_manifest_path ? dirname(claim.run_manifest_path) : null,
|
|
1780
|
+
questionSummary: {
|
|
1781
|
+
queuedCount: 0,
|
|
1782
|
+
latestQuestion: null
|
|
1783
|
+
},
|
|
1784
|
+
tracked: null,
|
|
1785
|
+
compatibilityState: claim.issue_state,
|
|
1786
|
+
providerLinearWorkerProof: null,
|
|
1787
|
+
providerRetryState: buildProviderRetryState(claim)
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
return {
|
|
1791
|
+
...base,
|
|
1792
|
+
lookupAliases: Array.from(new Set(base.lookupAliases.concat(buildProjectionLookupAliases({
|
|
1793
|
+
issueIdentifier: claim.issue_identifier,
|
|
1794
|
+
issueId: claim.issue_id,
|
|
1795
|
+
taskId: claim.task_id,
|
|
1796
|
+
runId: claim.run_id
|
|
1797
|
+
})))),
|
|
1798
|
+
rawStatus: claim.state,
|
|
1799
|
+
displayStatus: 'retrying',
|
|
1800
|
+
statusReason: claim.reason,
|
|
1801
|
+
updatedAt: claim.updated_at,
|
|
1802
|
+
summary: claim.reason ?? base.summary,
|
|
1803
|
+
lastError: claim.retry_error ?? base.lastError,
|
|
1804
|
+
latestEvent: retryLatestEvent ?? base.latestEvent,
|
|
1805
|
+
workspacePath: resolveRetryWorkspacePath(claim, controlWorkspacePath, base),
|
|
1806
|
+
compatibilityState: claim.issue_state ?? base.compatibilityState ?? null,
|
|
1807
|
+
providerRetryState: buildProviderRetryState(claim)
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
function resolveRetryWorkspacePath(claim, controlWorkspacePath, source) {
|
|
1811
|
+
const proofWorkspacePath = source?.providerLinearWorkerProof?.workspace_path ?? null;
|
|
1812
|
+
if (proofWorkspacePath) {
|
|
1813
|
+
return proofWorkspacePath;
|
|
1814
|
+
}
|
|
1815
|
+
if (source?.workspacePath &&
|
|
1816
|
+
(!controlWorkspacePath || source.workspacePath !== controlWorkspacePath)) {
|
|
1817
|
+
return source.workspacePath;
|
|
1818
|
+
}
|
|
1819
|
+
if (!controlWorkspacePath || !claim.task_id) {
|
|
1820
|
+
return null;
|
|
1821
|
+
}
|
|
1822
|
+
try {
|
|
1823
|
+
return resolveProviderWorkspacePath(controlWorkspacePath, claim.task_id);
|
|
1824
|
+
}
|
|
1825
|
+
catch {
|
|
1826
|
+
return null;
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
function readStringValue(record, ...keys) {
|
|
1830
|
+
for (const key of keys) {
|
|
1831
|
+
const value = record[key];
|
|
1832
|
+
if (typeof value === 'string') {
|
|
1833
|
+
const trimmed = value.trim();
|
|
1834
|
+
if (trimmed.length > 0) {
|
|
1835
|
+
return trimmed;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
return undefined;
|
|
1840
|
+
}
|
|
1841
|
+
function compareIsoTimestamp(left, right) {
|
|
1842
|
+
const leftValue = Date.parse(left ?? '');
|
|
1843
|
+
const rightValue = Date.parse(right ?? '');
|
|
1844
|
+
if (!Number.isFinite(leftValue) && !Number.isFinite(rightValue)) {
|
|
1845
|
+
return 0;
|
|
1846
|
+
}
|
|
1847
|
+
if (!Number.isFinite(leftValue)) {
|
|
1848
|
+
return -1;
|
|
1849
|
+
}
|
|
1850
|
+
if (!Number.isFinite(rightValue)) {
|
|
1851
|
+
return 1;
|
|
1852
|
+
}
|
|
1853
|
+
return leftValue - rightValue;
|
|
1854
|
+
}
|
|
1855
|
+
function buildProjectionLookupAliases(input) {
|
|
1856
|
+
const aliases = new Set();
|
|
1857
|
+
for (const candidate of [input.issueIdentifier, input.issueId, input.taskId, input.runId]) {
|
|
1858
|
+
if (candidate) {
|
|
1859
|
+
aliases.add(candidate);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
return Array.from(aliases);
|
|
1863
|
+
}
|
|
1864
|
+
async function resolveProviderSelectedManifestSnapshot(context) {
|
|
1865
|
+
const claim = selectProviderIntakeClaim(context.providerIntakeState);
|
|
1866
|
+
if (!claim?.run_manifest_path) {
|
|
1867
|
+
return null;
|
|
1868
|
+
}
|
|
1869
|
+
const preferredSnapshot = await readManifestSnapshotForPath(claim.run_manifest_path);
|
|
1870
|
+
if (!preferredSnapshot) {
|
|
1871
|
+
return null;
|
|
1872
|
+
}
|
|
1873
|
+
const currentTaskId = resolveTaskIdFromManifestPath(context.paths.manifestPath);
|
|
1874
|
+
const currentRunId = resolveRunIdFromManifestPath(context.paths.manifestPath);
|
|
1875
|
+
if (currentTaskId === 'local-mcp' && currentRunId === 'control-host') {
|
|
1876
|
+
return preferredSnapshot;
|
|
1877
|
+
}
|
|
1878
|
+
const providerControlHostTaskId = readStringValue(preferredSnapshot.manifestRecord, 'provider_control_host_task_id');
|
|
1879
|
+
const providerControlHostRunId = readStringValue(preferredSnapshot.manifestRecord, 'provider_control_host_run_id');
|
|
1880
|
+
if (providerControlHostTaskId !== currentTaskId ||
|
|
1881
|
+
providerControlHostRunId !== currentRunId) {
|
|
1882
|
+
return null;
|
|
1883
|
+
}
|
|
1884
|
+
return preferredSnapshot;
|
|
1885
|
+
}
|