@work-graph/cli 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/bin/work-graph.mjs +238 -0
- package/package.json +38 -0
- package/vendor/packages/design-tokens/generated/gripe-dark-default.css +67 -0
- package/vendor/packages/design-tokens/generated/marketplace-default.css +67 -0
- package/vendor/packages/design-tokens/generated/workgraph-dark.css +67 -0
- package/vendor/packages/workgraph-mcp/README.md +28 -0
- package/vendor/packages/workgraph-mcp/bin/workgraph-mcp.mjs +21 -0
- package/vendor/packages/workgraph-mcp/package.json +37 -0
- package/vendor/packages/workgraph-mcp/src/handlers.mjs +761 -0
- package/vendor/packages/workgraph-mcp/src/index.mjs +638 -0
- package/vendor/packages/workgraph-mcp/src/prompts.mjs +162 -0
- package/vendor/public/assets/workgraph-logo.svg +11 -0
- package/vendor/public/fonts/GraphikLCG/GraphikLCG-Medium.woff2 +0 -0
- package/vendor/public/fonts/GraphikLCG/GraphikLCG-Regular.woff2 +0 -0
- package/vendor/public/fonts/GraphikLCG/GraphikLCG-Semibold.woff2 +0 -0
- package/vendor/public/fonts/GraphikLCG/stylesheet.css +25 -0
- package/vendor/public/graph-canvas-lit-flow.css +154 -0
- package/vendor/public/graph-canvas-lit-flow.css.map +7 -0
- package/vendor/public/graph-canvas-lit-flow.js +8530 -0
- package/vendor/public/graph-canvas-lit-flow.js.map +7 -0
- package/vendor/src/agentBehaviorRulesAudit.mjs +168 -0
- package/vendor/src/agentBehaviorRulesBundle.mjs +144 -0
- package/vendor/src/agentRunApi.mjs +136 -0
- package/vendor/src/agentToolLoopGuard.mjs +88 -0
- package/vendor/src/agentWorkerClaudeProvider.mjs +288 -0
- package/vendor/src/agentWorkerCursorSdkProvider.mjs +156 -0
- package/vendor/src/agentWorkerLiveLoop.mjs +455 -0
- package/vendor/src/agentWorkerLocalCliProvider.mjs +217 -0
- package/vendor/src/agentWorkerLocalRunner.mjs +246 -0
- package/vendor/src/agentWorkerOpenAiProvider.mjs +459 -0
- package/vendor/src/analyticsPanelProjection.mjs +212 -0
- package/vendor/src/analyticsRecordStore.mjs +165 -0
- package/vendor/src/analyticsRecordWorkItems.mjs +104 -0
- package/vendor/src/architectureL1Canon.mjs +419 -0
- package/vendor/src/architectureLayout.mjs +229 -0
- package/vendor/src/architectureSnapshot.mjs +490 -0
- package/vendor/src/architectureViewsProjection.mjs +116 -0
- package/vendor/src/atomInspector.mjs +253 -0
- package/vendor/src/atomInspectorApi.mjs +130 -0
- package/vendor/src/auditGapMatrixRefresh.mjs +121 -0
- package/vendor/src/backlogSchemaLint.mjs +176 -0
- package/vendor/src/blockedOnebaseGoPreflightEval.mjs +100 -0
- package/vendor/src/bracketIrTraceSignal.mjs +93 -0
- package/vendor/src/bvcAtomParser.mjs +210 -0
- package/vendor/src/bvcDialectRegistry.mjs +86 -0
- package/vendor/src/bvcFileFormat.mjs +218 -0
- package/vendor/src/bvcFormatCli.mjs +55 -0
- package/vendor/src/bvcLintCli.mjs +48 -0
- package/vendor/src/bvcNewWritePolicy.mjs +70 -0
- package/vendor/src/charterPreflightPromoteGate.mjs +194 -0
- package/vendor/src/claimNoEligibleEval.mjs +205 -0
- package/vendor/src/closingAnalysisSuggest.mjs +59 -0
- package/vendor/src/codeGapAnalyzer.mjs +308 -0
- package/vendor/src/codeGapBacklogFeeder.mjs +82 -0
- package/vendor/src/codeGapDraftIntakeApi.mjs +307 -0
- package/vendor/src/codeGapOperatorProjection.mjs +60 -0
- package/vendor/src/codeSyntaxHighlight.mjs +123 -0
- package/vendor/src/codegenEvidence.mjs +187 -0
- package/vendor/src/compilerRoundTripCli.mjs +164 -0
- package/vendor/src/dagreGraphLayout.mjs +78 -0
- package/vendor/src/draftIntakePromotionRules.mjs +205 -0
- package/vendor/src/epicWorkScope.mjs +85 -0
- package/vendor/src/evalLiveLlmEnv.mjs +63 -0
- package/vendor/src/evidenceReadModel.mjs +167 -0
- package/vendor/src/gfsOverlayProjectPassport.mjs +235 -0
- package/vendor/src/globalStepPathToBvcReferences.mjs +196 -0
- package/vendor/src/goldenPath.mjs +69 -0
- package/vendor/src/graphCanvasLayout.mjs +464 -0
- package/vendor/src/graphCanvasLitFlow/client/graphCanvasMinimap.ts +261 -0
- package/vendor/src/graphCanvasLitFlow/client/graphCanvasSvgEdges.ts +259 -0
- package/vendor/src/graphCanvasLitFlow/client/graphCanvasTheme.css +152 -0
- package/vendor/src/graphCanvasLitFlow/client/graphCardNode.ts +328 -0
- package/vendor/src/graphCanvasLitFlow/client/mountGraphCanvasLitFlow.ts +322 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeLabels.mjs +58 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeRouter.mjs +142 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasLayoutProfile.mjs +32 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasNodeMetrics.mjs +45 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasProjection.mjs +115 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasProjectionToFlow.mjs +133 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasTraversal.mjs +77 -0
- package/vendor/src/graphCanvasLitFlow/layoutIntentRoadmapWorkStack.mjs +73 -0
- package/vendor/src/graphCanvasLitFlow/resolveGraphCanvasOverlaps.mjs +77 -0
- package/vendor/src/graphRagContextSlice.mjs +461 -0
- package/vendor/src/gvmVerifyWorkerGate.mjs +95 -0
- package/vendor/src/homeSnapshotApi.mjs +131 -0
- package/vendor/src/homeSnapshotProjection.mjs +275 -0
- package/vendor/src/inboxEventStream.mjs +140 -0
- package/vendor/src/intentComposerApi.mjs +245 -0
- package/vendor/src/intentGraphGbcSliceBoundary.mjs +258 -0
- package/vendor/src/intentGraphProjection.mjs +208 -0
- package/vendor/src/intentHierarchy.mjs +241 -0
- package/vendor/src/intentNodeLint.mjs +107 -0
- package/vendor/src/intentNodeRuntime.mjs +185 -0
- package/vendor/src/intentRoadmapCanvas.mjs +393 -0
- package/vendor/src/intentRoadmapEpicProjection.mjs +122 -0
- package/vendor/src/intentRoadmapMermaid.mjs +165 -0
- package/vendor/src/intentRoadmapProjection.mjs +85 -0
- package/vendor/src/intentTreeLint.mjs +114 -0
- package/vendor/src/intentTreeMigration.mjs +150 -0
- package/vendor/src/intentTreeWorkItems.mjs +227 -0
- package/vendor/src/kanbanBoardProjection.mjs +58 -0
- package/vendor/src/languageAdapterRegistry.mjs +180 -0
- package/vendor/src/languageAdapters/goAdapter.mjs +62 -0
- package/vendor/src/languageAdapters/jsTsAdapter.mjs +60 -0
- package/vendor/src/languageAdapters/jsonYamlAdapter.mjs +103 -0
- package/vendor/src/languageAdapters/onebaseOsAdapter.mjs +55 -0
- package/vendor/src/languageAdapters/plaintextAdapter.mjs +36 -0
- package/vendor/src/languageAdapters/shared.mjs +68 -0
- package/vendor/src/languageAdapters/stepAdapter.mjs +81 -0
- package/vendor/src/lintPlanWorkAlignment.mjs +136 -0
- package/vendor/src/loopHintRepeatToolEval.mjs +153 -0
- package/vendor/src/lowcodeScaffoldCli.mjs +386 -0
- package/vendor/src/markdownDocumentRender.mjs +208 -0
- package/vendor/src/memoryPanelProjection.mjs +116 -0
- package/vendor/src/memoryRecordWriter.mjs +243 -0
- package/vendor/src/memoryWorkerSlice.mjs +238 -0
- package/vendor/src/migrateStepToBvc.mjs +133 -0
- package/vendor/src/missionControlServerHandlers.mjs +195 -0
- package/vendor/src/missionControlUiClient.mjs +278 -0
- package/vendor/src/onebaseCliCapabilityProbe.mjs +107 -0
- package/vendor/src/onebaseCliRunner.mjs +145 -0
- package/vendor/src/onebaseGrossProfitStaticVerify.mjs +98 -0
- package/vendor/src/onebaseParityEvidenceSync.mjs +88 -0
- package/vendor/src/onebasePvrgGraphNodes.mjs +257 -0
- package/vendor/src/onebaseRestEvidenceAdapter.mjs +216 -0
- package/vendor/src/onebaseVectorDslCodegenReadiness.mjs +137 -0
- package/vendor/src/onebaseWorkItemTemplate.mjs +154 -0
- package/vendor/src/onebaseWorkerTools.mjs +586 -0
- package/vendor/src/operatorShellProjection.mjs +102 -0
- package/vendor/src/pipelineProseRender.mjs +180 -0
- package/vendor/src/pipelineStageLint.mjs +118 -0
- package/vendor/src/promptRulesEditorApi.mjs +174 -0
- package/vendor/src/promptRulesProjection.mjs +134 -0
- package/vendor/src/pvrg/bladeAdapter.mjs +40 -0
- package/vendor/src/pvrgTaskScope.mjs +152 -0
- package/vendor/src/releaseGateMatrix.mjs +188 -0
- package/vendor/src/schematicView.mjs +305 -0
- package/vendor/src/seedAnalyticsRecord.mjs +217 -0
- package/vendor/src/semanticSearchBm25.mjs +103 -0
- package/vendor/src/semanticSearchExcerpts.mjs +68 -0
- package/vendor/src/semanticSearchTfidfVector.mjs +86 -0
- package/vendor/src/semanticSearchWorkflow.mjs +366 -0
- package/vendor/src/stepAtomFormatter.mjs +413 -0
- package/vendor/src/stepGraphSlice.mjs +318 -0
- package/vendor/src/ui/atoms/badge.mjs +40 -0
- package/vendor/src/ui/atoms/badgeClient.mjs +32 -0
- package/vendor/src/ui/atoms/button.mjs +114 -0
- package/vendor/src/ui/atoms/buttonClient.mjs +49 -0
- package/vendor/src/ui/atoms/icon.mjs +23 -0
- package/vendor/src/ui/atoms/input.mjs +38 -0
- package/vendor/src/ui/atoms/modal.mjs +44 -0
- package/vendor/src/ui/atoms/select.mjs +98 -0
- package/vendor/src/ui/backlogShellButtons.mjs +238 -0
- package/vendor/src/ui/htmlEscape.mjs +11 -0
- package/vendor/src/ui/molecules/rating.mjs +48 -0
- package/vendor/src/ui/molecules/tabs.mjs +70 -0
- package/vendor/src/ui/organisms/modal.mjs +1 -0
- package/vendor/src/ui/pages/uiKitPage.mjs +147 -0
- package/vendor/src/ui/workItemStatusTone.mjs +36 -0
- package/vendor/src/unifiedLinkageProjection.mjs +264 -0
- package/vendor/src/verificationLoop.mjs +206 -0
- package/vendor/src/workGraphBacklogPersist.mjs +234 -0
- package/vendor/src/workGraphBacklogUiServer.mjs +9192 -0
- package/vendor/src/workGraphBoundedTargetFileRead.mjs +178 -0
- package/vendor/src/workGraphCycleSlice.mjs +184 -0
- package/vendor/src/workGraphDaemonTick.mjs +307 -0
- package/vendor/src/workGraphDaemonWatch.mjs +157 -0
- package/vendor/src/workGraphEngineRoot.mjs +136 -0
- package/vendor/src/workGraphInstallLayout.mjs +65 -0
- package/vendor/src/workGraphLlmUsefulnessEval.mjs +611 -0
- package/vendor/src/workGraphPhasePromoteReadyQueue.mjs +159 -0
- package/vendor/src/workGraphProjectHost.mjs +149 -0
- package/vendor/src/workGraphProjectInit.mjs +392 -0
- package/vendor/src/workGraphPromoteReadyApi.mjs +115 -0
- package/vendor/src/workGraphRecoveryPolicy.mjs +124 -0
- package/vendor/src/workGraphRunnerQueueProjection.mjs +187 -0
- package/vendor/src/workGraphRuntime.mjs +1008 -0
- package/vendor/src/workGraphToolSurfaceAudit.mjs +372 -0
- package/vendor/src/workGraphToolTransportRuntime.mjs +195 -0
- package/vendor/src/workGraphWorkerProvider.mjs +600 -0
- package/vendor/src/workItemBvcQuality.mjs +262 -0
- package/vendor/src/workItemCreateAnalysis.mjs +157 -0
- package/vendor/src/workItemDecisionPipeline.mjs +278 -0
- package/vendor/src/workItemEpicCascade.mjs +176 -0
- package/vendor/src/workItemExecutionGate.mjs +78 -0
- package/vendor/src/workItemHierarchy.mjs +226 -0
- package/vendor/src/workItemProseLint.mjs +133 -0
- package/vendor/src/workItemTextRusify.mjs +794 -0
- package/vendor/src/workItemTraceEnvelope.mjs +158 -0
- package/vendor/src/workItemUiReferences.mjs +272 -0
- package/vendor/src/workflowEpicGrouping.mjs +67 -0
- package/vendor/src/workflowTreeProjection.mjs +53 -0
- package/vendor/src/workspaceRegistry.mjs +150 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import { appendFile, readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
buildWorkerInputFromTask,
|
|
7
|
+
} from './agentWorkerLocalRunner.mjs';
|
|
8
|
+
import { loadAgentBehaviorRulesBundle } from './agentBehaviorRulesBundle.mjs';
|
|
9
|
+
import { buildWorkerInputWithRoleHandoff } from './agentWorkerOpenAiProvider.mjs';
|
|
10
|
+
import { AGENT_LOOP_PHASES } from './goldenPath.mjs';
|
|
11
|
+
import { readWorkItemsFromRepo } from './intentTreeWorkItems.mjs';
|
|
12
|
+
import { runWorkerWithProvider } from './workGraphWorkerProvider.mjs';
|
|
13
|
+
import {
|
|
14
|
+
WorkGraphPolicyError,
|
|
15
|
+
claimNext,
|
|
16
|
+
claimWorkItemWithLease,
|
|
17
|
+
parseWorkItems,
|
|
18
|
+
recordEvidence,
|
|
19
|
+
transitionStatus,
|
|
20
|
+
} from './workGraphRuntime.mjs';
|
|
21
|
+
import { evaluateWorkItemExecutionGate } from './workItemExecutionGate.mjs';
|
|
22
|
+
|
|
23
|
+
export { AGENT_LOOP_PHASES as AGENT_LIVE_LOOP_PHASES };
|
|
24
|
+
|
|
25
|
+
const DEFAULT_JOURNAL_PATH = 'work/worker-runs.jsonl';
|
|
26
|
+
|
|
27
|
+
export function formatWorkerEvidenceLine(evidence, output) {
|
|
28
|
+
if (typeof evidence === 'string') {
|
|
29
|
+
return evidence.trim();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!evidence || typeof evidence !== 'object') {
|
|
33
|
+
return `worker-run: runId=${output.runId} status=${output.status}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const parts = [
|
|
37
|
+
'worker-run',
|
|
38
|
+
`runId=${output.runId}`,
|
|
39
|
+
evidence.kind ? `kind=${evidence.kind}` : '',
|
|
40
|
+
evidence.source ? `source=${evidence.source}` : '',
|
|
41
|
+
evidence.result ? `result=${evidence.result}` : '',
|
|
42
|
+
evidence.summary ? `summary=${String(evidence.summary).trim()}` : '',
|
|
43
|
+
].filter(Boolean);
|
|
44
|
+
|
|
45
|
+
return parts.join(' ');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function buildWorkerRunSummary(output, loopMeta = {}) {
|
|
49
|
+
return {
|
|
50
|
+
runId: output.runId,
|
|
51
|
+
taskId: output.taskId,
|
|
52
|
+
status: output.status,
|
|
53
|
+
provider: loopMeta.provider ?? 'local-runner',
|
|
54
|
+
selectionMode: loopMeta.selectionMode ?? null,
|
|
55
|
+
explicitProvider: loopMeta.explicitProvider ?? false,
|
|
56
|
+
usedFallback: loopMeta.usedFallback ?? false,
|
|
57
|
+
fallbackTrail: loopMeta.fallbackTrail ?? [],
|
|
58
|
+
transitionProposal: output.transitionRequest ?? null,
|
|
59
|
+
appliedTransition: loopMeta.appliedTransition ?? null,
|
|
60
|
+
summary: output.patchSummary?.summary || output.failureReason || output.status,
|
|
61
|
+
recordedAt: loopMeta.recordedAt ?? new Date(0).toISOString(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function applyWorkerOutputToItem(item, output) {
|
|
66
|
+
let current = item;
|
|
67
|
+
const evidenceLines = Array.isArray(output.evidence) && output.evidence.length > 0
|
|
68
|
+
? output.evidence.map((entry) => formatWorkerEvidenceLine(entry, output))
|
|
69
|
+
: [formatWorkerEvidenceLine(null, output)];
|
|
70
|
+
|
|
71
|
+
for (const line of evidenceLines) {
|
|
72
|
+
current = recordEvidence(current, line);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const transitionProposal = {
|
|
76
|
+
status: output.transitionRequest?.status ?? (output.status === 'succeeded' ? 'verify' : 'blocked'),
|
|
77
|
+
reason: output.transitionRequest?.reason ?? output.failureReason ?? '',
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
let appliedTransition = null;
|
|
81
|
+
let transitionError = null;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
if (output.status === 'succeeded') {
|
|
85
|
+
current = transitionStatus(current, transitionProposal.status, {
|
|
86
|
+
reason: transitionProposal.reason,
|
|
87
|
+
});
|
|
88
|
+
appliedTransition = transitionProposal.status;
|
|
89
|
+
} else {
|
|
90
|
+
const blockerReason = transitionProposal.reason || output.failureReason || 'worker run failed';
|
|
91
|
+
current = transitionStatus(current, 'blocked', {
|
|
92
|
+
reason: blockerReason,
|
|
93
|
+
blocker: blockerReason,
|
|
94
|
+
});
|
|
95
|
+
appliedTransition = 'blocked';
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
transitionError = error instanceof WorkGraphPolicyError || error instanceof Error
|
|
99
|
+
? error.message
|
|
100
|
+
: String(error);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
updatedItem: current,
|
|
105
|
+
transitionProposal,
|
|
106
|
+
appliedTransition,
|
|
107
|
+
transitionError,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function runAgentWorkerLiveLoop(items, options = {}) {
|
|
112
|
+
const steps = [];
|
|
113
|
+
const pool = items.map(cloneItem);
|
|
114
|
+
|
|
115
|
+
steps.push({ phase: 'observe', detail: 'read snapshot', itemCount: pool.length });
|
|
116
|
+
|
|
117
|
+
const selected = options.taskId
|
|
118
|
+
? pool.find((item) => item.id === options.taskId)
|
|
119
|
+
: claimNext(pool);
|
|
120
|
+
|
|
121
|
+
if (!selected) {
|
|
122
|
+
return {
|
|
123
|
+
ok: false,
|
|
124
|
+
error: 'no_claimable_task',
|
|
125
|
+
steps,
|
|
126
|
+
finalItems: pool,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const gate = evaluateWorkItemExecutionGate(selected);
|
|
131
|
+
if (!gate.allowed) {
|
|
132
|
+
return {
|
|
133
|
+
ok: false,
|
|
134
|
+
error: gate.code,
|
|
135
|
+
message: gate.message,
|
|
136
|
+
taskId: selected.id,
|
|
137
|
+
steps,
|
|
138
|
+
finalItems: pool,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const runId = options.runId ?? `live-loop-${selected.id}`;
|
|
143
|
+
const claimResult = claimWorkItemWithLease(selected, {
|
|
144
|
+
claimRunId: runId,
|
|
145
|
+
targetStatus: 'claimed',
|
|
146
|
+
nowMs: options.nowMs,
|
|
147
|
+
leaseMs: options.claimLeaseMs,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (!claimResult.ok) {
|
|
151
|
+
return {
|
|
152
|
+
ok: false,
|
|
153
|
+
error: claimResult.error,
|
|
154
|
+
taskId: selected.id,
|
|
155
|
+
claimedBy: claimResult.claimedBy ?? null,
|
|
156
|
+
leaseUntil: claimResult.leaseUntil ?? null,
|
|
157
|
+
steps,
|
|
158
|
+
finalItems: pool,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
steps.push({ phase: 'plan', detail: 'select task ' + selected.id, taskId: selected.id });
|
|
163
|
+
|
|
164
|
+
let current = claimResult.item;
|
|
165
|
+
replaceItem(pool, selected.id, current);
|
|
166
|
+
steps.push({
|
|
167
|
+
phase: 'claim',
|
|
168
|
+
detail: claimResult.idempotent ? 'reuse active claim lease' : 'transition to claimed',
|
|
169
|
+
taskId: current.id,
|
|
170
|
+
status: current.status,
|
|
171
|
+
claimRunId: claimResult.claimRunId,
|
|
172
|
+
leaseUntil: claimResult.leaseUntil,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (claimResult.idempotent && typeof options.runWorker !== 'function') {
|
|
176
|
+
return {
|
|
177
|
+
ok: true,
|
|
178
|
+
taskId: current.id,
|
|
179
|
+
steps,
|
|
180
|
+
idempotentClaim: true,
|
|
181
|
+
claimRunId: claimResult.claimRunId,
|
|
182
|
+
leaseUntil: claimResult.leaseUntil,
|
|
183
|
+
finalItems: pool,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const explicitProvider = options.provider ?? null;
|
|
188
|
+
const useRoleHandoff = explicitProvider === 'openai' || explicitProvider === 'openai-compatible';
|
|
189
|
+
const handoffExtras = useRoleHandoff
|
|
190
|
+
? buildWorkerInputWithRoleHandoff(current, { runId, ...(options.handoff ?? {}) })
|
|
191
|
+
: {};
|
|
192
|
+
|
|
193
|
+
const behaviorBundle = options.behaviorRulesBundle ?? (
|
|
194
|
+
options.skipBehaviorRules
|
|
195
|
+
? null
|
|
196
|
+
: await loadAgentBehaviorRulesBundle({ cwd: options.cwd ?? process.cwd() })
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const behaviorHints = behaviorBundle?.ok
|
|
200
|
+
? {
|
|
201
|
+
behaviorRuleIds: behaviorBundle.ruleIds,
|
|
202
|
+
behaviorRulesPrompt: behaviorBundle.promptSlice,
|
|
203
|
+
}
|
|
204
|
+
: {};
|
|
205
|
+
|
|
206
|
+
const input = buildWorkerInputFromTask(current, {
|
|
207
|
+
runId,
|
|
208
|
+
policy: {
|
|
209
|
+
mode: handoffExtras.policy?.mode ?? 'dry-run',
|
|
210
|
+
allowShell: false,
|
|
211
|
+
allowNetwork: false,
|
|
212
|
+
allowFileWrite: false,
|
|
213
|
+
timeoutMs: handoffExtras.policy?.timeoutMs ?? 0,
|
|
214
|
+
...(options.workerInput?.policy ?? {}),
|
|
215
|
+
},
|
|
216
|
+
workGraphItems: pool,
|
|
217
|
+
graphRag: options.graphRag,
|
|
218
|
+
providerHints: {
|
|
219
|
+
...(handoffExtras.providerHints ?? {}),
|
|
220
|
+
...behaviorHints,
|
|
221
|
+
...(options.workerInput?.providerHints ?? {}),
|
|
222
|
+
},
|
|
223
|
+
allowedTools: options.workerInput?.allowedTools,
|
|
224
|
+
memorySlice: options.workerInput?.memorySlice,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
let providerResult = null;
|
|
228
|
+
let output;
|
|
229
|
+
|
|
230
|
+
if (typeof options.runWorker === 'function') {
|
|
231
|
+
output = options.runWorker(input);
|
|
232
|
+
if (output && typeof output.then === 'function') {
|
|
233
|
+
output = await output;
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
providerResult = await runWorkerWithProvider(input, {
|
|
237
|
+
provider: explicitProvider ?? undefined,
|
|
238
|
+
providerOptions: options.providerOptions,
|
|
239
|
+
selectionOptions: options.selectionOptions,
|
|
240
|
+
enableFallback: options.enableFallback,
|
|
241
|
+
maxFallbackAttempts: options.maxFallbackAttempts,
|
|
242
|
+
});
|
|
243
|
+
output = providerResult.output;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const resolvedProviderId = providerResult?.providerId ?? explicitProvider ?? 'local';
|
|
247
|
+
const selectionMode = providerResult?.selectionRationale?.selectionMode ?? (explicitProvider ? 'explicit_provider' : 'capability_score');
|
|
248
|
+
|
|
249
|
+
steps.push({
|
|
250
|
+
phase: 'act',
|
|
251
|
+
detail: providerResult?.usedFallback
|
|
252
|
+
? `run ${resolvedProviderId} worker after fallback`
|
|
253
|
+
: explicitProvider
|
|
254
|
+
? `run ${explicitProvider} worker`
|
|
255
|
+
: `run ${resolvedProviderId} worker (${selectionMode})`,
|
|
256
|
+
taskId: current.id,
|
|
257
|
+
workerStatus: output.status,
|
|
258
|
+
providerId: resolvedProviderId,
|
|
259
|
+
selectionMode,
|
|
260
|
+
usedFallback: providerResult?.usedFallback ?? false,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const applied = applyWorkerOutputToItem(current, output);
|
|
264
|
+
current = applied.updatedItem;
|
|
265
|
+
replaceItem(pool, current.id, current);
|
|
266
|
+
|
|
267
|
+
steps.push({
|
|
268
|
+
phase: 'verify',
|
|
269
|
+
detail: 'apply worker output evidence',
|
|
270
|
+
evidenceCount: current.evidence.length,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
steps.push({
|
|
274
|
+
phase: 'record',
|
|
275
|
+
detail: 'propose transition',
|
|
276
|
+
transitionProposal: applied.transitionProposal,
|
|
277
|
+
appliedTransition: applied.appliedTransition,
|
|
278
|
+
transitionError: applied.transitionError,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
steps.push({ phase: 'stop', detail: 'live loop complete', taskId: current.id });
|
|
282
|
+
|
|
283
|
+
const recordedAt = options.recordedAt ?? new Date().toISOString();
|
|
284
|
+
const workerRunSummary = buildWorkerRunSummary(output, {
|
|
285
|
+
appliedTransition: applied.appliedTransition,
|
|
286
|
+
recordedAt,
|
|
287
|
+
provider: resolvedProviderId,
|
|
288
|
+
selectionMode,
|
|
289
|
+
explicitProvider: providerResult?.explicitProvider ?? Boolean(explicitProvider),
|
|
290
|
+
usedFallback: providerResult?.usedFallback ?? false,
|
|
291
|
+
fallbackTrail: providerResult?.fallbackTrail ?? [],
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
ok: output.status === 'succeeded' && applied.transitionError === null,
|
|
296
|
+
error: applied.transitionError ?? (output.status === 'succeeded' ? null : output.failureReason),
|
|
297
|
+
taskId: current.id,
|
|
298
|
+
steps,
|
|
299
|
+
workerInput: input,
|
|
300
|
+
workerOutput: output,
|
|
301
|
+
providerResult,
|
|
302
|
+
transitionProposal: applied.transitionProposal,
|
|
303
|
+
appliedTransition: applied.appliedTransition,
|
|
304
|
+
transitionError: applied.transitionError,
|
|
305
|
+
workerRunSummary,
|
|
306
|
+
finalItems: pool,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export async function runAgentWorkerLiveLoopFromBacklogText(backlogText, options = {}) {
|
|
311
|
+
return runAgentWorkerLiveLoop(parseWorkItems(backlogText), options);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export async function runAgentWorkerLiveLoopFromBacklogFile(options = {}) {
|
|
315
|
+
const items = await readWorkItemsFromRepo(options);
|
|
316
|
+
return runAgentWorkerLiveLoop(items, options);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export async function readWorkerRunJournal(options = {}) {
|
|
320
|
+
const journalPath = resolve(options.cwd ?? process.cwd(), options.journalPath ?? DEFAULT_JOURNAL_PATH);
|
|
321
|
+
|
|
322
|
+
try {
|
|
323
|
+
const text = await readFile(journalPath, 'utf8');
|
|
324
|
+
return text
|
|
325
|
+
.split(/\r?\n/u)
|
|
326
|
+
.map((line) => line.trim())
|
|
327
|
+
.filter(Boolean)
|
|
328
|
+
.map((line) => JSON.parse(line));
|
|
329
|
+
} catch (error) {
|
|
330
|
+
if (error && typeof error === 'object' && error.code === 'ENOENT') {
|
|
331
|
+
return [];
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export async function appendWorkerRunJournal(entry, options = {}) {
|
|
339
|
+
const journalPath = resolve(options.cwd ?? process.cwd(), options.journalPath ?? DEFAULT_JOURNAL_PATH);
|
|
340
|
+
await appendFile(journalPath, `${JSON.stringify(entry)}\n`, 'utf8');
|
|
341
|
+
return journalPath;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function cloneItem(item) {
|
|
345
|
+
return {
|
|
346
|
+
...item,
|
|
347
|
+
dependsOn: [...item.dependsOn],
|
|
348
|
+
targetFiles: [...item.targetFiles],
|
|
349
|
+
evidence: [...item.evidence],
|
|
350
|
+
checks: [...item.checks],
|
|
351
|
+
labels: { ...item.labels },
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function replaceItem(pool, id, nextItem) {
|
|
356
|
+
const index = pool.findIndex((item) => item.id === id);
|
|
357
|
+
if (index === -1) {
|
|
358
|
+
return pool;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
pool[index] = nextItem;
|
|
362
|
+
return pool;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function parseCliArgs(argv) {
|
|
366
|
+
const args = argv.slice(2);
|
|
367
|
+
const options = {
|
|
368
|
+
once: false,
|
|
369
|
+
dryRun: true,
|
|
370
|
+
writeJournal: true,
|
|
371
|
+
taskId: undefined,
|
|
372
|
+
provider: undefined,
|
|
373
|
+
explicitProvider: false,
|
|
374
|
+
backlogPath: undefined,
|
|
375
|
+
journalPath: DEFAULT_JOURNAL_PATH,
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
379
|
+
const arg = args[index];
|
|
380
|
+
|
|
381
|
+
if (arg === '--once') {
|
|
382
|
+
options.once = true;
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (arg === '--dry-run') {
|
|
387
|
+
options.dryRun = true;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (arg === '--provider') {
|
|
392
|
+
options.provider = args[index + 1] ?? 'local';
|
|
393
|
+
options.explicitProvider = true;
|
|
394
|
+
index += 1;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (arg === '--no-journal') {
|
|
399
|
+
options.writeJournal = false;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (arg === '--backlog') {
|
|
404
|
+
options.backlogPath = args[index + 1];
|
|
405
|
+
index += 1;
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (arg === '--journal') {
|
|
410
|
+
options.journalPath = args[index + 1] ?? DEFAULT_JOURNAL_PATH;
|
|
411
|
+
index += 1;
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (!arg.startsWith('--') && options.taskId === undefined) {
|
|
416
|
+
options.taskId = arg;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return options;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
424
|
+
const cli = parseCliArgs(process.argv);
|
|
425
|
+
|
|
426
|
+
if (!cli.once) {
|
|
427
|
+
console.error('Usage: node src/agentWorkerLiveLoop.mjs --once [--provider local|openai] [--backlog path/to/backlog.bvc] [--journal work/worker-runs.jsonl] [taskId]');
|
|
428
|
+
process.exitCode = 1;
|
|
429
|
+
} else {
|
|
430
|
+
const result = await runAgentWorkerLiveLoopFromBacklogFile({
|
|
431
|
+
taskId: cli.taskId,
|
|
432
|
+
backlogPath: cli.backlogPath,
|
|
433
|
+
provider: cli.explicitProvider ? cli.provider : undefined,
|
|
434
|
+
workerInput: cli.dryRun ? { policy: { mode: 'dry-run' } } : {},
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
if (cli.writeJournal && result.workerRunSummary) {
|
|
438
|
+
await appendWorkerRunJournal(result.workerRunSummary, { journalPath: cli.journalPath });
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
console.log(JSON.stringify({
|
|
442
|
+
ok: result.ok,
|
|
443
|
+
taskId: result.taskId,
|
|
444
|
+
error: result.error,
|
|
445
|
+
appliedTransition: result.appliedTransition,
|
|
446
|
+
transitionProposal: result.transitionProposal,
|
|
447
|
+
workerRunSummary: result.workerRunSummary,
|
|
448
|
+
steps: result.steps?.map((step) => step.phase),
|
|
449
|
+
}, null, 2));
|
|
450
|
+
|
|
451
|
+
if (!result.ok) {
|
|
452
|
+
process.exitCode = 1;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
import { VERIFICATION_MATRIX } from './verificationLoop.mjs';
|
|
4
|
+
|
|
5
|
+
const INPUT_SCHEMA = 'agent-worker.input.v1';
|
|
6
|
+
const OUTPUT_SCHEMA = 'agent-worker.output.v1';
|
|
7
|
+
|
|
8
|
+
const ALLOWLISTED_COMMANDS = new Set(
|
|
9
|
+
VERIFICATION_MATRIX
|
|
10
|
+
.map((row) => row.command)
|
|
11
|
+
.filter((command) => typeof command === 'string' && command.trim() !== '' && !command.startsWith('manual')),
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export function resolveLocalCliProviderEnv(options = {}) {
|
|
15
|
+
const env = options.env ?? process.env;
|
|
16
|
+
return {
|
|
17
|
+
enabled: (options.enabled ?? env.IOHASC_LOCAL_CLI_WORKER) === '1',
|
|
18
|
+
cwd: String(options.cwd ?? env.IOHASC_LOCAL_CLI_CWD ?? process.cwd()).trim(),
|
|
19
|
+
timeoutMs: Number(options.timeoutMs ?? env.IOHASC_LOCAL_CLI_TIMEOUT_MS ?? 900_000),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function resolveAllowlistedVerificationCommand(task) {
|
|
24
|
+
if (!task || typeof task !== 'object') {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const taskId = String(task.id ?? '').trim();
|
|
29
|
+
if (taskId === '') {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const matches = VERIFICATION_MATRIX.filter((row) => row.gateTaskIds.includes(taskId));
|
|
34
|
+
if (matches.length === 0) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return matches.find((row) => ALLOWLISTED_COMMANDS.has(row.command)) ?? null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function assertAllowlistedCliCommand(command) {
|
|
42
|
+
const normalized = String(command ?? '').trim();
|
|
43
|
+
if (!ALLOWLISTED_COMMANDS.has(normalized)) {
|
|
44
|
+
throw new Error(`command not in verification allowlist: ${normalized}`);
|
|
45
|
+
}
|
|
46
|
+
return normalized;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function executeAllowlistedCliCommand(command, options = {}) {
|
|
50
|
+
const allowlistedCommand = assertAllowlistedCliCommand(command);
|
|
51
|
+
const cwd = options.cwd ?? process.cwd();
|
|
52
|
+
const runCommand = options.runCommand ?? defaultRunCommand;
|
|
53
|
+
return runCommand(allowlistedCommand, { cwd, timeoutMs: options.timeoutMs ?? 900_000 });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function runLocalCliWorker(input, options = {}) {
|
|
57
|
+
const validationError = validateLocalCliWorkerInput(input);
|
|
58
|
+
if (validationError !== null) {
|
|
59
|
+
return buildLocalCliFailureOutput(input, validationError, 'code_failure');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const env = resolveLocalCliProviderEnv(options);
|
|
63
|
+
if (!env.enabled && options.requireLive !== false) {
|
|
64
|
+
return buildLocalCliFailureOutput(
|
|
65
|
+
input,
|
|
66
|
+
'Local CLI provider skipped: set IOHASC_LOCAL_CLI_WORKER=1 for live path',
|
|
67
|
+
'skipped',
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (input.policy?.allowShell !== true) {
|
|
72
|
+
return buildLocalCliFailureOutput(
|
|
73
|
+
input,
|
|
74
|
+
'Local CLI provider blocked: Worker Input policy.allowShell must be true',
|
|
75
|
+
'blocked',
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const matrixRow = resolveAllowlistedVerificationCommand(input.task);
|
|
80
|
+
if (!matrixRow) {
|
|
81
|
+
return buildLocalCliFailureOutput(
|
|
82
|
+
input,
|
|
83
|
+
`No verification allowlist entry for task ${input.task.id}`,
|
|
84
|
+
'blocked',
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const result = executeAllowlistedCliCommand(matrixRow.command, {
|
|
90
|
+
cwd: options.cwd ?? env.cwd,
|
|
91
|
+
timeoutMs: env.timeoutMs,
|
|
92
|
+
runCommand: options.runCommand,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (result.exitCode !== 0) {
|
|
96
|
+
return buildLocalCliFailureOutput(
|
|
97
|
+
input,
|
|
98
|
+
`Allowlisted command failed (${matrixRow.command}): exit ${result.exitCode}`,
|
|
99
|
+
'env_blocker',
|
|
100
|
+
{
|
|
101
|
+
command: matrixRow.command,
|
|
102
|
+
verificationId: matrixRow.id,
|
|
103
|
+
stdout: result.stdout,
|
|
104
|
+
stderr: result.stderr,
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
schema: OUTPUT_SCHEMA,
|
|
111
|
+
runId: input.runId,
|
|
112
|
+
taskId: input.task.id,
|
|
113
|
+
status: 'succeeded',
|
|
114
|
+
patchSummary: {
|
|
115
|
+
changedFiles: [],
|
|
116
|
+
summary: `Allowlisted verification command succeeded: ${matrixRow.command}`,
|
|
117
|
+
},
|
|
118
|
+
evidence: [{
|
|
119
|
+
kind: 'verification_command',
|
|
120
|
+
source: 'local-cli',
|
|
121
|
+
result: 'succeeded',
|
|
122
|
+
summary: `${matrixRow.id}: ${matrixRow.command}`,
|
|
123
|
+
verificationId: matrixRow.id,
|
|
124
|
+
command: matrixRow.command,
|
|
125
|
+
exitCode: result.exitCode,
|
|
126
|
+
}],
|
|
127
|
+
transitionRequest: {
|
|
128
|
+
status: 'verify',
|
|
129
|
+
reason: `verification allowlist command passed (${matrixRow.id})`,
|
|
130
|
+
},
|
|
131
|
+
logs: [{
|
|
132
|
+
level: 'info',
|
|
133
|
+
message: `Executed allowlisted command ${matrixRow.command} with exit code ${result.exitCode}`,
|
|
134
|
+
}],
|
|
135
|
+
failureReason: '',
|
|
136
|
+
retryAdvice: '',
|
|
137
|
+
};
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
140
|
+
return buildLocalCliFailureOutput(input, `Local CLI execution failed: ${message}`, 'env_blocker');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function defaultRunCommand(command, options = {}) {
|
|
145
|
+
const result = spawnSync(command, {
|
|
146
|
+
cwd: options.cwd,
|
|
147
|
+
encoding: 'utf8',
|
|
148
|
+
shell: true,
|
|
149
|
+
timeout: options.timeoutMs,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
exitCode: result.status ?? 1,
|
|
154
|
+
stdout: result.stdout ?? '',
|
|
155
|
+
stderr: result.stderr ?? '',
|
|
156
|
+
signal: result.signal ?? null,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function validateLocalCliWorkerInput(input) {
|
|
161
|
+
if (!input || typeof input !== 'object') {
|
|
162
|
+
return 'input must be an object';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (input.schema !== INPUT_SCHEMA) {
|
|
166
|
+
return `unsupported input schema: ${String(input.schema)}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!input.task || typeof input.task.id !== 'string' || input.task.id.trim() === '') {
|
|
170
|
+
return 'task.id must be a non-empty string';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (input.policy?.allowNetwork === true) {
|
|
174
|
+
return 'local-cli provider MVP rejects allowNetwork policy flag';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (input.policy?.allowFileWrite === true) {
|
|
178
|
+
return 'local-cli provider MVP rejects allowFileWrite policy flag';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function buildLocalCliFailureOutput(input, failureReason, failureClass, details = {}) {
|
|
185
|
+
const runId = typeof input?.runId === 'string' && input.runId.trim() !== '' ? input.runId : 'local-cli-invalid-input';
|
|
186
|
+
const taskId = typeof input?.task?.id === 'string' && input.task.id.trim() !== '' ? input.task.id : '';
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
schema: OUTPUT_SCHEMA,
|
|
190
|
+
runId,
|
|
191
|
+
taskId,
|
|
192
|
+
status: 'failed',
|
|
193
|
+
patchSummary: {
|
|
194
|
+
changedFiles: [],
|
|
195
|
+
summary: failureClass === 'skipped'
|
|
196
|
+
? 'Local CLI provider skipped (env-gated mode).'
|
|
197
|
+
: 'Local CLI provider failed before producing verification evidence.',
|
|
198
|
+
},
|
|
199
|
+
evidence: [{
|
|
200
|
+
kind: 'verification_command',
|
|
201
|
+
source: 'local-cli',
|
|
202
|
+
result: 'failed',
|
|
203
|
+
summary: failureReason,
|
|
204
|
+
failureClass,
|
|
205
|
+
...details,
|
|
206
|
+
}],
|
|
207
|
+
transitionRequest: {
|
|
208
|
+
status: 'blocked',
|
|
209
|
+
reason: failureReason,
|
|
210
|
+
},
|
|
211
|
+
logs: [{ level: failureClass === 'skipped' ? 'info' : 'error', message: failureReason }],
|
|
212
|
+
failureReason,
|
|
213
|
+
retryAdvice: failureClass === 'skipped'
|
|
214
|
+
? 'Set IOHASC_LOCAL_CLI_WORKER=1 and allowShell=true with a verification-matrix gate for the task.'
|
|
215
|
+
: 'Fix verification command environment or choose a different provider, then retry.',
|
|
216
|
+
};
|
|
217
|
+
}
|