@amistio/cli 0.1.24 → 0.1.25

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/dist/index.js CHANGED
@@ -24,6 +24,10 @@ var itemTypeSchema = z.enum([
24
24
  "securityScan",
25
25
  "securityFinding",
26
26
  "securityPostureSnapshot",
27
+ "testProfile",
28
+ "testQualityScan",
29
+ "testQualityFinding",
30
+ "implementationTestGate",
27
31
  "appEvaluationScan",
28
32
  "appEvaluationFinding",
29
33
  "implementationVerification",
@@ -79,9 +83,89 @@ var workStatusSchema = z.enum([
79
83
  ]);
80
84
  var sourceSchema = z.enum(["web", "repo", "generated", "runner"]);
81
85
  var executionModeSchema = z.enum(["localRunner", "cloudSandbox"]);
82
- var workKindSchema = z.enum(["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification"]);
86
+ var workKindSchema = z.enum(["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification", "testQualityScan", "implementationTestGate", "runnerCommand"]);
87
+ var autopilotModeSchema = z.enum(["disabled", "enabled", "paused"]);
88
+ var autopilotClassificationOutcomeSchema = z.enum(["safeAutopilotEligible", "requiresReview", "blocked", "unsafe"]);
89
+ var autopilotCandidateTypeSchema = z.enum([
90
+ "generatedDraftApproval",
91
+ "projectBrainSync",
92
+ "issueDiagnosis",
93
+ "issueFixHandoff",
94
+ "securityPostureScan",
95
+ "securityRemediationHandoff",
96
+ "appEvaluationScan",
97
+ "appEvaluationPlanHandoff",
98
+ "appEvaluationCleanup",
99
+ "projectContextRefresh",
100
+ "impactPreview",
101
+ "implementationQueue",
102
+ "implementationVerification",
103
+ "testQualityScan",
104
+ "implementationTestGate",
105
+ "requeueWork",
106
+ "runnerCommand"
107
+ ]);
108
+ var autopilotReasonCodeSchema = z.enum([
109
+ "allowedLowRisk",
110
+ "disabled",
111
+ "paused",
112
+ "runnerRequired",
113
+ "runnerIncompatible",
114
+ "repositoryRequired",
115
+ "staleRevision",
116
+ "duplicateInFlight",
117
+ "budgetExceeded",
118
+ "protectedSurface",
119
+ "destructiveAction",
120
+ "rawSourceUpload",
121
+ "secretRisk",
122
+ "primaryCheckoutMutation",
123
+ "policyBlocked",
124
+ "reviewRequired",
125
+ "cooldownActive",
126
+ "expired",
127
+ "failureBudgetExceeded",
128
+ "verificationFailed",
129
+ "redactionHit",
130
+ "candidateTypeNotAllowed",
131
+ "riskTierExceeded",
132
+ "unsafePath",
133
+ "schemaInvalid",
134
+ "notAdditive",
135
+ "scopeMismatch",
136
+ "sizeLimitExceeded",
137
+ "worktreeIsolationRequired",
138
+ "verificationRequired",
139
+ "organizationPolicyRequired",
140
+ "unsafe"
141
+ ]);
142
+ var autopilotGuardCheckSchema = z.object({
143
+ name: z.string().trim().min(1).max(120),
144
+ status: z.enum(["passed", "failed", "warning"]),
145
+ summary: z.string().trim().min(1).max(600)
146
+ });
83
147
  var implementationHandoffStatusSchema = z.enum(["notStarted", "noChanges", "prReady", "blocked", "failed"]);
84
148
  var implementationHandoffCleanupStatusSchema = z.enum(["notApplicable", "pending", "completed", "failed"]);
149
+ var safeRepoPathSchema = z.string().trim().min(1).max(300).refine((value) => {
150
+ if (value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value)) {
151
+ return false;
152
+ }
153
+ return !value.split(/[\\/]+/).includes("..");
154
+ }, "Path must be repository-relative without traversal");
155
+ var implementationHandoffArtifactStatusSchema = z.enum(["included", "skipped", "blocked"]);
156
+ var implementationHandoffArtifactSchema = z.object({
157
+ documentId: z.string().trim().min(1).max(200),
158
+ title: z.string().trim().min(1).max(200).optional(),
159
+ repoPath: safeRepoPathSchema,
160
+ contentFormat: documentContentFormatSchema,
161
+ status: implementationHandoffArtifactStatusSchema,
162
+ message: z.string().trim().min(1).max(600).optional()
163
+ }).strict();
164
+ var implementationHandoffArtifactsSchema = z.object({
165
+ included: z.array(implementationHandoffArtifactSchema).max(50).default([]),
166
+ skipped: z.array(implementationHandoffArtifactSchema).max(50).default([]),
167
+ blocked: z.array(implementationHandoffArtifactSchema).max(50).default([])
168
+ }).strict();
85
169
  var implementationVerificationOutcomeSchema = z.enum(["verifiedImplemented", "partiallyImplemented", "notImplemented", "inconclusive", "verificationBlocked"]);
86
170
  var implementationVerificationStatusSchema = z.enum(["queued", "running", "completed", "failed", "blocked", "stale"]);
87
171
  var implementationProofStatusSchema = z.enum(["unverified", "verificationQueued", "verificationRunning", "verifiedImplemented", "partiallyImplemented", "notImplemented", "inconclusive", "verificationBlocked", "humanOverride"]);
@@ -96,12 +180,97 @@ var implementationHandoffSchema = z.object({
96
180
  prUrl: z.string().trim().url().max(500).optional(),
97
181
  cleanupStatus: implementationHandoffCleanupStatusSchema.optional(),
98
182
  cleanupMessage: z.string().trim().min(1).max(600).optional(),
183
+ artifacts: implementationHandoffArtifactsSchema.optional(),
99
184
  message: z.string().trim().min(1).max(600).optional(),
100
185
  error: z.string().trim().min(1).max(1200).optional()
101
186
  }).strict();
102
187
  var generatedDraftStatusSchema = z.enum(["queued", "generating", "reviewing", "approved", "rejected", "changesRequested", "failed"]);
103
188
  var assistantQuestionModeSchema = z.enum(["brainOnly", "sourceAware"]);
104
189
  var impactRiskLevelSchema = z.enum(["low", "medium", "high", "critical"]);
190
+ var autopilotBudgetWindowSchema = z.object({
191
+ name: z.string().trim().min(1).max(80),
192
+ limit: z.number().int().nonnegative(),
193
+ used: z.number().int().nonnegative(),
194
+ remaining: z.number().int().nonnegative().optional(),
195
+ windowStartedAt: isoDateTimeSchema,
196
+ windowEndsAt: isoDateTimeSchema
197
+ }).strict();
198
+ var autopilotPolicySnapshotSchema = z.object({
199
+ policyVersion: z.string().trim().min(1).max(80),
200
+ mode: autopilotModeSchema,
201
+ enabledByUserId: z.string().min(1).optional(),
202
+ repositoryLinkId: z.string().min(1).optional(),
203
+ runnerId: z.string().min(1).optional(),
204
+ allowedWorkKinds: z.array(workKindSchema).max(20).default([]),
205
+ allowedCandidateTypes: z.array(autopilotCandidateTypeSchema).max(30).default([]),
206
+ maxRiskLevel: impactRiskLevelSchema.default("low"),
207
+ dailyWorkLimit: z.number().int().positive().max(100).optional(),
208
+ dailyPrLimit: z.number().int().nonnegative().max(100).optional(),
209
+ concurrentWorkLimit: z.number().int().positive().max(20).optional(),
210
+ runnerMinuteBudget: z.number().int().nonnegative().max(1440).optional(),
211
+ failureBudget: z.number().int().nonnegative().max(50).optional(),
212
+ expiresAt: isoDateTimeSchema.optional(),
213
+ reviewAfter: isoDateTimeSchema.optional(),
214
+ cooldownUntil: isoDateTimeSchema.optional(),
215
+ pausedReason: z.string().trim().min(1).max(600).optional(),
216
+ budgetWindows: z.array(autopilotBudgetWindowSchema).max(10).default([])
217
+ }).strict();
218
+ var autopilotCandidateLinksSchema = z.object({
219
+ workItemId: z.string().min(1).optional(),
220
+ documentId: z.string().min(1).optional(),
221
+ generatedDraftId: z.string().min(1).optional(),
222
+ issueId: z.string().min(1).optional(),
223
+ securityScanId: z.string().min(1).optional(),
224
+ securityFindingId: z.string().min(1).optional(),
225
+ appEvaluationScanId: z.string().min(1).optional(),
226
+ appEvaluationFindingId: z.string().min(1).optional(),
227
+ implementationVerificationId: z.string().min(1).optional(),
228
+ testQualityScanId: z.string().min(1).optional(),
229
+ testQualityFindingId: z.string().min(1).optional(),
230
+ implementationTestGateId: z.string().min(1).optional(),
231
+ projectContextRefreshId: z.string().min(1).optional(),
232
+ runnerCommandId: z.string().min(1).optional(),
233
+ repositoryLinkId: z.string().min(1).optional(),
234
+ runnerId: z.string().min(1).optional(),
235
+ branchName: z.string().trim().min(1).max(200).optional(),
236
+ worktreeKey: z.string().trim().min(1).max(300).optional(),
237
+ pullRequestUrl: z.string().trim().url().max(500).optional()
238
+ }).strict();
239
+ var autopilotCandidateActionSchema = z.object({
240
+ candidateId: z.string().trim().min(1).max(160),
241
+ candidateType: autopilotCandidateTypeSchema,
242
+ subjectId: z.string().trim().min(1).max(200),
243
+ workKind: workKindSchema.optional(),
244
+ riskLevel: impactRiskLevelSchema.default("low"),
245
+ summary: z.string().trim().min(1).max(600),
246
+ safeRepoPaths: z.array(safeRepoPathSchema).max(50).default([]),
247
+ evidenceSummaries: z.array(z.string().trim().min(1).max(300)).max(20).default([]),
248
+ links: autopilotCandidateLinksSchema.default({}),
249
+ policySnapshot: autopilotPolicySnapshotSchema.optional(),
250
+ outcome: autopilotClassificationOutcomeSchema.optional(),
251
+ reasonCodes: z.array(autopilotReasonCodeSchema).max(30).optional(),
252
+ guardChecks: z.array(autopilotGuardCheckSchema).max(30).optional()
253
+ }).strict();
254
+ var autopilotAuthorizationSchema = z.object({
255
+ authorizationId: z.string().trim().min(1).max(160),
256
+ policyVersion: z.string().trim().min(1).max(80),
257
+ mode: autopilotModeSchema,
258
+ outcome: autopilotClassificationOutcomeSchema,
259
+ candidateId: z.string().trim().min(1).max(160).optional(),
260
+ candidateType: autopilotCandidateTypeSchema.optional(),
261
+ subjectId: z.string().trim().min(1).max(200).optional(),
262
+ riskLevel: impactRiskLevelSchema,
263
+ reasonCodes: z.array(autopilotReasonCodeSchema).min(1),
264
+ guardChecks: z.array(autopilotGuardCheckSchema).default([]),
265
+ policySnapshot: autopilotPolicySnapshotSchema.optional(),
266
+ budgetWindows: z.array(autopilotBudgetWindowSchema).max(10).default([]),
267
+ linkedRecords: autopilotCandidateLinksSchema.optional(),
268
+ evidenceSummaries: z.array(z.string().trim().min(1).max(300)).max(20).default([]),
269
+ authorizedAt: isoDateTimeSchema.optional(),
270
+ authorizedBy: z.literal("autopilot").optional(),
271
+ reviewedByUserId: z.string().min(1).optional(),
272
+ summary: z.string().trim().min(1).max(600)
273
+ }).strict();
105
274
  var impactReportStatusSchema = z.enum(["queued", "running", "completed", "failed", "stale"]);
106
275
  var issueCategorySchema = z.enum(["bug", "regression", "brokenWorkflow", "security", "operational", "other"]);
107
276
  var issueSeveritySchema = z.enum(["low", "medium", "high", "critical"]);
@@ -114,6 +283,22 @@ var securityFindingSeveritySchema = z.enum(["info", "low", "medium", "high", "cr
114
283
  var securityFindingConfidenceSchema = z.enum(["low", "medium", "high"]);
115
284
  var securityFindingStatusSchema = z.enum(["open", "planReady", "approved", "changesRequested", "dismissed", "acceptedRisk", "remediationQueued", "resolved", "failed"]);
116
285
  var securityApprovalStateSchema = z.enum(["proposed", "approved", "changesRequested", "dismissed", "acceptedRisk", "implemented", "blocked"]);
286
+ var testProfileStatusSchema = z.enum(["detected", "reviewReady", "approved", "disabled", "stale"]);
287
+ var testPackageManagerSchema = z.enum(["pnpm", "npm", "yarn", "bun", "dotnet", "maven", "gradle", "mixed", "unknown"]);
288
+ var testCommandKindSchema = z.enum(["verify", "test", "coverage", "lint", "typecheck", "build", "focused"]);
289
+ var testCommandSourceSchema = z.enum(["detected", "approved", "manual"]);
290
+ var testCoverageMetricStatusSchema = z.enum(["met", "belowThreshold", "missing", "blocked", "unknown"]);
291
+ var testCommandStatusSchema = z.enum(["passed", "failed", "blocked", "skipped", "missing"]);
292
+ var testRedactionStatusSchema = z.enum(["clean", "redacted", "blocked"]);
293
+ var testQualityScanStatusSchema = z.enum(["queued", "running", "completed", "failed", "stale", "skipped"]);
294
+ var testQualityFindingCategorySchema = z.enum(["missingTests", "missingCoverage", "missingCommand", "lowCoverage", "failingTests", "failingQuality", "failingQualityCheck", "staleScan", "blockedEnvironment", "weakTests", "flakyTests", "unverifiedImplementation", "testGap", "other"]);
295
+ var testQualityFindingSeveritySchema = z.enum(["info", "low", "medium", "high", "critical"]);
296
+ var testQualityFindingConfidenceSchema = z.enum(["low", "medium", "high"]);
297
+ var testQualityFindingStatusSchema = z.enum(["open", "planReady", "approved", "changesRequested", "dismissed", "acceptedRisk", "implementationQueued", "resolved", "failed"]);
298
+ var testQualityApprovalStateSchema = z.enum(["proposed", "approved", "changesRequested", "dismissed", "acceptedRisk", "implemented", "blocked"]);
299
+ var implementationTestGateStatusSchema = z.enum(["queued", "running", "passed", "failed", "blocked", "overridden", "stale"]);
300
+ var implementationTestGateOutcomeSchema = z.enum(["passed", "failed", "blocked", "missingTests", "belowThreshold", "overridden"]);
301
+ var implementationTestGateSourceSchema = z.enum(["manual", "runner", "completion", "prHandoff", "push", "system"]);
117
302
  var appEvaluationScanStatusSchema = z.enum(["queued", "running", "completed", "failed", "stale", "skipped"]);
118
303
  var appEvaluationFindingCategorySchema = z.enum(["verification", "productDrift", "planCleanup", "promptRot", "missingMemory", "releaseReadiness", "ux", "accessibility", "performance", "reliability", "securityPosture", "other"]);
119
304
  var appEvaluationFindingSeveritySchema = z.enum(["info", "low", "medium", "high", "critical"]);
@@ -122,6 +307,10 @@ var appEvaluationFindingStatusSchema = z.enum(["open", "planReady", "approved",
122
307
  var appEvaluationApprovalStateSchema = z.enum(["proposed", "approved", "changesRequested", "dismissed", "acceptedRisk", "implemented", "blocked"]);
123
308
  var appEvaluationPlanLifecycleActionSchema = z.enum(["createPlan", "updatePlan", "markCompleted", "markImplemented", "markSuperseded", "markBlocked", "archive", "keepActive", "none"]);
124
309
  var activityEventTypeSchema = z.enum([
310
+ "autopilotAuthorized",
311
+ "autopilotReviewRequired",
312
+ "autopilotBlocked",
313
+ "autopilotPaused",
125
314
  "commandSubmitted",
126
315
  "generationQueued",
127
316
  "generationCompleted",
@@ -161,6 +350,22 @@ var activityEventTypeSchema = z.enum([
161
350
  "appEvaluationFindingReviewed",
162
351
  "appEvaluationPlanCreated",
163
352
  "appEvaluationImplementationQueued",
353
+ "testProfileDetected",
354
+ "testProfileUpdated",
355
+ "testQualityScanQueued",
356
+ "testQualityScanStarted",
357
+ "testQualityScanCompleted",
358
+ "testQualityScanFailed",
359
+ "testQualityFindingOpened",
360
+ "testQualityFindingReviewed",
361
+ "testQualityPlanCreated",
362
+ "implementationTestGateQueued",
363
+ "implementationTestGateStarted",
364
+ "implementationTestGatePassed",
365
+ "implementationTestGateFailed",
366
+ "implementationTestGateBlocked",
367
+ "implementationTestGateOverride",
368
+ "ciWorkflowRetired",
164
369
  "workRequeueRequested",
165
370
  "workRequeueQueued",
166
371
  "workRequeueBlocked",
@@ -288,6 +493,7 @@ var runnerResourceUsageSchema = z.object({
288
493
  systemLoadAverage5m: z.number().nonnegative().optional(),
289
494
  systemLoadAverage15m: z.number().nonnegative().optional()
290
495
  });
496
+ var runnerClaimLaneIdSchema = z.string().trim().min(1).max(80);
291
497
  var workIsolationModeSchema = z.enum(["none", "primaryCheckout", "branch", "gitWorktree"]);
292
498
  var repositoryLinkSourceSchema = z.enum(["web", "cli"]);
293
499
  var repositoryCloneStatusSchema = z.enum(["notCloned", "cloned", "validated", "failed"]);
@@ -370,6 +576,25 @@ var repositoryLinkItemSchema = baseItemSchema.extend({
370
576
  cloneStatus: repositoryCloneStatusSchema.optional(),
371
577
  autoSyncEnabled: z.boolean().optional(),
372
578
  appEvaluationEnabled: z.boolean().optional(),
579
+ testQualityEnabled: z.boolean().optional(),
580
+ autopilotMode: autopilotModeSchema.optional(),
581
+ autopilotPolicyVersion: z.string().trim().min(1).max(80).optional(),
582
+ autopilotSafeWorkKinds: z.array(workKindSchema).optional(),
583
+ autopilotAllowedCandidateTypes: z.array(autopilotCandidateTypeSchema).optional(),
584
+ autopilotMaxRiskLevel: impactRiskLevelSchema.optional(),
585
+ autopilotRunnerId: z.string().min(1).optional(),
586
+ autopilotPausedReason: z.string().trim().min(1).max(600).optional(),
587
+ autopilotDailyWorkLimit: z.number().int().positive().max(100).optional(),
588
+ autopilotDailyPrLimit: z.number().int().nonnegative().max(100).optional(),
589
+ autopilotConcurrentWorkLimit: z.number().int().positive().max(20).optional(),
590
+ autopilotRunnerMinuteBudget: z.number().int().nonnegative().max(1440).optional(),
591
+ autopilotFailureBudget: z.number().int().nonnegative().max(50).optional(),
592
+ autopilotExpiresAt: isoDateTimeSchema.optional(),
593
+ autopilotReviewAfter: isoDateTimeSchema.optional(),
594
+ autopilotCooldownUntil: isoDateTimeSchema.optional(),
595
+ autopilotBudgetWindows: z.array(autopilotBudgetWindowSchema).max(10).optional(),
596
+ autopilotUpdatedByUserId: z.string().min(1).optional(),
597
+ autopilotUpdatedAt: isoDateTimeSchema.optional(),
373
598
  lastValidatedAt: isoDateTimeSchema.optional(),
374
599
  status: z.enum(["active", "revoked"]).default("active")
375
600
  });
@@ -441,6 +666,9 @@ var workItemSchema = baseItemSchema.extend({
441
666
  appEvaluationScanId: z.string().min(1).optional(),
442
667
  appEvaluationFindingId: z.string().min(1).optional(),
443
668
  implementationVerificationId: z.string().min(1).optional(),
669
+ testQualityScanId: z.string().min(1).optional(),
670
+ testQualityFindingId: z.string().min(1).optional(),
671
+ implementationTestGateId: z.string().min(1).optional(),
444
672
  projectContextRefreshId: z.string().min(1).optional(),
445
673
  contextMissId: z.string().min(1).optional(),
446
674
  sourceWorkItemId: z.string().min(1).optional(),
@@ -468,6 +696,7 @@ var workItemSchema = baseItemSchema.extend({
468
696
  claimedByRunnerId: z.string().optional(),
469
697
  pairedByUserId: z.string().min(1).optional(),
470
698
  machineId: z.string().min(1).optional(),
699
+ claimLaneId: runnerClaimLaneIdSchema.optional(),
471
700
  claimLeaseId: z.string().min(1).optional(),
472
701
  claimAttempt: z.number().int().nonnegative().optional(),
473
702
  leaseExpiresAt: isoDateTimeSchema.optional(),
@@ -488,6 +717,12 @@ var workItemSchema = baseItemSchema.extend({
488
717
  sessionDecision: sessionDecisionSchema.optional(),
489
718
  sessionDecisionReason: z.string().min(1).optional(),
490
719
  implementationHandoff: implementationHandoffSchema.optional(),
720
+ autopilotAuthorization: autopilotAuthorizationSchema.optional(),
721
+ autopilotAuthorizationId: z.string().min(1).optional(),
722
+ autopilotCandidateId: z.string().min(1).optional(),
723
+ autopilotCandidateType: autopilotCandidateTypeSchema.optional(),
724
+ autopilotClassificationOutcome: autopilotClassificationOutcomeSchema.optional(),
725
+ autopilotPolicyVersion: z.string().trim().min(1).max(80).optional(),
491
726
  lastStatusMessage: z.string().optional(),
492
727
  lastStatusAt: isoDateTimeSchema
493
728
  });
@@ -507,6 +742,8 @@ var runnerHeartbeatItemSchema = baseItemSchema.extend({
507
742
  supportedWorkKinds: z.array(workKindSchema).optional(),
508
743
  supportsBranchIsolation: z.boolean().optional(),
509
744
  supportsGitWorktreeIsolation: z.boolean().optional(),
745
+ maxConcurrentWork: z.number().int().min(1).max(4).optional(),
746
+ activeClaimLaneIds: z.array(runnerClaimLaneIdSchema).max(4).optional(),
510
747
  currentWorkItemId: z.string().min(1).optional(),
511
748
  currentImplementationScopeId: z.string().min(1).optional(),
512
749
  currentWorktreeKey: z.string().min(1).optional(),
@@ -565,6 +802,7 @@ var runnerExecutionLogItemSchema = baseItemSchema.extend({
565
802
  executionWorktreeKey: z.string().min(1).optional(),
566
803
  isolationMode: workIsolationModeSchema.optional(),
567
804
  repositoryLockId: z.string().min(1).optional(),
805
+ claimLaneId: runnerClaimLaneIdSchema.optional(),
568
806
  claimLeaseId: z.string().min(1).optional(),
569
807
  status: z.enum(["claimed", "running", "completed", "failed", "blocked", "idle"]),
570
808
  executionMode: executionModeSchema.optional(),
@@ -796,12 +1034,7 @@ var projectContextFreshnessSchema = z.enum(["fresh", "stale", "partial", "missin
796
1034
  var projectContextMapStatusSchema = z.enum(["draft", "proposed", "approved", "stale", "archived"]);
797
1035
  var projectContextRefreshStatusSchema = z.enum(["queued", "running", "completed", "failed", "skipped"]);
798
1036
  var contextMissStatusSchema = z.enum(["open", "resolved", "dismissed"]);
799
- var projectContextRepoPathSchema = z.string().trim().min(1).max(300).refine((value) => {
800
- if (value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value)) {
801
- return false;
802
- }
803
- return !value.split(/[\\/]+/).includes("..");
804
- }, "Path must be repository-relative without traversal");
1037
+ var projectContextRepoPathSchema = safeRepoPathSchema;
805
1038
  var projectContextCitationSchema = assistantCitationSchema.extend({
806
1039
  repoPath: projectContextRepoPathSchema.optional()
807
1040
  });
@@ -1269,6 +1502,179 @@ var securityPostureSnapshotItemSchema = baseItemSchema.extend({
1269
1502
  nextScheduledAt: isoDateTimeSchema.optional(),
1270
1503
  summary: z.string().trim().min(1).max(2e3).optional()
1271
1504
  });
1505
+ var testCoverageThresholdsSchema = z.object({
1506
+ lines: z.number().min(0).max(100).optional(),
1507
+ statements: z.number().min(0).max(100).optional(),
1508
+ branches: z.number().min(0).max(100).optional(),
1509
+ functions: z.number().min(0).max(100).optional()
1510
+ }).strict();
1511
+ var testProfileCommandSchema = z.object({
1512
+ commandId: z.string().trim().min(1).max(160),
1513
+ kind: testCommandKindSchema,
1514
+ label: z.string().trim().min(1).max(200),
1515
+ source: testCommandSourceSchema.default("detected"),
1516
+ packageName: z.string().trim().min(1).max(200).optional(),
1517
+ scriptName: z.string().trim().min(1).max(120).optional(),
1518
+ tool: z.string().trim().min(1).max(120).optional(),
1519
+ workingDirectory: projectContextRepoPathSchema.optional(),
1520
+ required: z.boolean().default(false),
1521
+ estimatedDurationSeconds: z.number().int().positive().max(7200).optional()
1522
+ }).strict();
1523
+ var testProfileItemSchema = baseItemSchema.extend({
1524
+ type: z.literal("testProfile"),
1525
+ projectId: z.string().min(1),
1526
+ testProfileId: z.string().min(1),
1527
+ repositoryLinkId: z.string().min(1),
1528
+ status: testProfileStatusSchema,
1529
+ enabled: z.boolean().default(true),
1530
+ detectedPackageManager: testPackageManagerSchema.default("unknown"),
1531
+ packageManagers: z.array(testPackageManagerSchema).default([]),
1532
+ commands: z.array(testProfileCommandSchema).max(40).default([]),
1533
+ coverageThresholds: testCoverageThresholdsSchema.default({}),
1534
+ defaultWholeAppCommandId: z.string().trim().min(1).max(160).optional(),
1535
+ focusedCommandIds: z.array(z.string().trim().min(1).max(160)).max(20).default([]),
1536
+ lastDetectedAt: isoDateTimeSchema.optional(),
1537
+ lastReviewedRevision: z.string().trim().min(1).max(160).optional(),
1538
+ reviewedByUserId: z.string().min(1).optional(),
1539
+ reviewedAt: isoDateTimeSchema.optional()
1540
+ });
1541
+ var testCommandSummarySchema = z.object({
1542
+ commandId: z.string().trim().min(1).max(160).optional(),
1543
+ kind: testCommandKindSchema,
1544
+ label: z.string().trim().min(1).max(200),
1545
+ status: testCommandStatusSchema,
1546
+ durationMs: z.number().int().nonnegative().optional(),
1547
+ exitCode: z.number().int().min(0).max(255).optional(),
1548
+ summary: z.string().trim().min(1).max(1200),
1549
+ outputExcerpt: z.string().trim().min(1).max(1200).optional(),
1550
+ safePaths: z.array(projectContextRepoPathSchema).max(30).default([])
1551
+ }).strict();
1552
+ var testCoverageSummarySchema = z.object({
1553
+ status: testCoverageMetricStatusSchema,
1554
+ lines: z.number().min(0).max(100).optional(),
1555
+ statements: z.number().min(0).max(100).optional(),
1556
+ branches: z.number().min(0).max(100).optional(),
1557
+ functions: z.number().min(0).max(100).optional(),
1558
+ thresholds: testCoverageThresholdsSchema.default({}),
1559
+ reportPath: projectContextRepoPathSchema.optional(),
1560
+ summary: z.string().trim().min(1).max(1200).optional()
1561
+ }).strict();
1562
+ var testRedactionStateSchema = z.object({
1563
+ status: testRedactionStatusSchema.default("clean"),
1564
+ redactedFields: z.array(z.string().trim().min(1).max(120)).max(40).default([]),
1565
+ summary: z.string().trim().min(1).max(600).optional()
1566
+ }).strict();
1567
+ var testQualityFindingResultSchema = z.object({
1568
+ title: z.string().trim().min(1).max(200),
1569
+ category: testQualityFindingCategorySchema,
1570
+ severity: testQualityFindingSeveritySchema,
1571
+ confidence: testQualityFindingConfidenceSchema.default("medium"),
1572
+ summary: z.string().trim().min(1).max(2e3),
1573
+ affectedSurfaces: z.array(z.string().trim().min(1).max(300)).default([]),
1574
+ evidence: z.array(z.string().trim().min(1).max(600)).default([]),
1575
+ safePaths: z.array(projectContextRepoPathSchema).max(30).default([]),
1576
+ suggestedAction: z.string().trim().min(1).max(4e3),
1577
+ verificationPlan: z.array(z.string().trim().min(1).max(300)).min(1),
1578
+ proposedPlanTitle: z.string().trim().min(1).max(200).optional(),
1579
+ proposedPlanRepoPath: projectContextRepoPathSchema.optional(),
1580
+ proposedPlanContent: z.string().trim().min(1).max(3e4).optional(),
1581
+ dedupeKey: z.string().trim().min(1).max(240).optional(),
1582
+ status: testQualityFindingStatusSchema.default("open")
1583
+ }).strict();
1584
+ var testQualityScanResultSchema = z.object({
1585
+ summary: z.string().trim().min(1).max(2e3),
1586
+ profile: testProfileItemSchema.omit({ id: true, type: true, schemaVersion: true, accountId: true, projectId: true, createdAt: true, updatedAt: true }).optional(),
1587
+ commandSummaries: z.array(testCommandSummarySchema).max(40).default([]),
1588
+ coverage: testCoverageSummarySchema.optional(),
1589
+ findings: z.array(testQualityFindingResultSchema).max(50).default([]),
1590
+ blockedReasons: z.array(z.string().trim().min(1).max(600)).max(20).default([]),
1591
+ redactionState: testRedactionStateSchema.default({ status: "clean", redactedFields: [] }),
1592
+ verificationPlan: z.array(z.string().trim().min(1).max(300)).min(1),
1593
+ warnings: z.array(z.string().trim().min(1).max(600)).default([])
1594
+ }).strict();
1595
+ var testQualityScanItemSchema = baseItemSchema.extend({
1596
+ type: z.literal("testQualityScan"),
1597
+ projectId: z.string().min(1),
1598
+ testQualityScanId: z.string().min(1),
1599
+ scheduledDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
1600
+ source: z.enum(["runner", "manual"]),
1601
+ status: testQualityScanStatusSchema,
1602
+ repositoryLinkId: z.string().min(1).optional(),
1603
+ runnerId: z.string().min(1).optional(),
1604
+ workItemId: z.string().min(1).optional(),
1605
+ testProfileId: z.string().min(1).optional(),
1606
+ findingIds: z.array(z.string().min(1)).default([]),
1607
+ summary: z.string().trim().min(1).max(2e3).optional(),
1608
+ result: testQualityScanResultSchema.optional(),
1609
+ startedAt: isoDateTimeSchema.optional(),
1610
+ completedAt: isoDateTimeSchema.optional(),
1611
+ failedAt: isoDateTimeSchema.optional(),
1612
+ error: z.string().max(1e3).optional()
1613
+ });
1614
+ var testQualityFindingItemSchema = baseItemSchema.extend({
1615
+ type: z.literal("testQualityFinding"),
1616
+ projectId: z.string().min(1),
1617
+ testQualityFindingId: z.string().min(1),
1618
+ testQualityScanId: z.string().min(1).optional(),
1619
+ implementationTestGateId: z.string().min(1).optional(),
1620
+ repositoryLinkId: z.string().min(1).optional(),
1621
+ title: z.string().trim().min(1).max(200),
1622
+ category: testQualityFindingCategorySchema,
1623
+ severity: testQualityFindingSeveritySchema,
1624
+ confidence: testQualityFindingConfidenceSchema.default("medium"),
1625
+ status: testQualityFindingStatusSchema,
1626
+ approvalState: testQualityApprovalStateSchema.default("proposed"),
1627
+ summary: z.string().trim().min(1).max(2e3),
1628
+ affectedSurfaces: z.array(z.string().trim().min(1).max(300)).default([]),
1629
+ evidence: z.array(z.string().trim().min(1).max(600)).default([]),
1630
+ safePaths: z.array(projectContextRepoPathSchema).max(30).default([]),
1631
+ suggestedAction: z.string().trim().min(1).max(4e3),
1632
+ verificationPlan: z.array(z.string().trim().min(1).max(300)).min(1),
1633
+ dedupeKey: z.string().trim().min(1).max(240).optional(),
1634
+ proposedPlanDocumentId: z.string().min(1).optional(),
1635
+ implementationWorkItemId: z.string().min(1).optional(),
1636
+ reviewNotes: z.string().trim().min(1).optional(),
1637
+ reviewedByUserId: z.string().min(1).optional(),
1638
+ reviewedAt: isoDateTimeSchema.optional(),
1639
+ resolvedAt: isoDateTimeSchema.optional(),
1640
+ lastReviewAction: z.enum(["approve", "requestChanges", "dismiss", "acceptRisk", "reopen"]).optional()
1641
+ });
1642
+ var implementationTestGateResultSchema = z.object({
1643
+ outcome: implementationTestGateOutcomeSchema,
1644
+ summary: z.string().trim().min(1).max(2e3),
1645
+ commandSummaries: z.array(testCommandSummarySchema).max(40).default([]),
1646
+ coverage: testCoverageSummarySchema.optional(),
1647
+ findings: z.array(testQualityFindingResultSchema).max(50).default([]),
1648
+ testGapJustification: z.string().trim().min(1).max(1200).optional(),
1649
+ blockedReasons: z.array(z.string().trim().min(1).max(600)).max(20).default([]),
1650
+ redactionState: testRedactionStateSchema.default({ status: "clean", redactedFields: [] }),
1651
+ verificationPlan: z.array(z.string().trim().min(1).max(300)).default([]),
1652
+ warnings: z.array(z.string().trim().min(1).max(600)).default([])
1653
+ }).strict();
1654
+ var implementationTestGateItemSchema = baseItemSchema.extend({
1655
+ type: z.literal("implementationTestGate"),
1656
+ projectId: z.string().min(1),
1657
+ implementationTestGateId: z.string().min(1),
1658
+ status: implementationTestGateStatusSchema,
1659
+ source: implementationTestGateSourceSchema.default("system"),
1660
+ sourceWorkItemId: z.string().min(1),
1661
+ gateWorkItemId: z.string().min(1).optional(),
1662
+ repositoryLinkId: z.string().min(1).optional(),
1663
+ runnerId: z.string().min(1).optional(),
1664
+ planDocumentId: z.string().min(1).optional(),
1665
+ planDocumentRevision: z.number().int().nonnegative().optional(),
1666
+ testProfileId: z.string().min(1).optional(),
1667
+ outcome: implementationTestGateOutcomeSchema.optional(),
1668
+ result: implementationTestGateResultSchema.optional(),
1669
+ summary: z.string().trim().min(1).max(2e3).optional(),
1670
+ startedAt: isoDateTimeSchema.optional(),
1671
+ completedAt: isoDateTimeSchema.optional(),
1672
+ failedAt: isoDateTimeSchema.optional(),
1673
+ overriddenByUserId: z.string().min(1).optional(),
1674
+ overrideReason: z.string().trim().min(1).max(1e3).optional(),
1675
+ overriddenAt: isoDateTimeSchema.optional(),
1676
+ error: z.string().max(1e3).optional()
1677
+ });
1272
1678
  var appEvaluationFindingResultSchema = z.object({
1273
1679
  title: z.string().trim().min(1).max(200),
1274
1680
  category: appEvaluationFindingCategorySchema,
@@ -1358,13 +1764,19 @@ var activityEventItemSchema = baseItemSchema.extend({
1358
1764
  relatedAppEvaluationScanId: z.string().min(1).optional(),
1359
1765
  relatedAppEvaluationFindingId: z.string().min(1).optional(),
1360
1766
  relatedImplementationVerificationId: z.string().min(1).optional(),
1767
+ relatedTestQualityScanId: z.string().min(1).optional(),
1768
+ relatedTestQualityFindingId: z.string().min(1).optional(),
1769
+ relatedImplementationTestGateId: z.string().min(1).optional(),
1361
1770
  relatedProjectContextRefreshId: z.string().min(1).optional(),
1362
1771
  relatedProjectSystemDiagramId: z.string().min(1).optional(),
1363
1772
  relatedContextMissId: z.string().min(1).optional(),
1773
+ relatedAutopilotAuthorizationId: z.string().min(1).optional(),
1774
+ relatedAutopilotCandidateId: z.string().min(1).optional(),
1364
1775
  generatedDraftId: z.string().min(1).optional(),
1365
1776
  runnerId: z.string().min(1).optional(),
1366
1777
  repositoryLinkId: z.string().min(1).optional(),
1367
1778
  runnerLogId: z.string().min(1).optional(),
1779
+ claimLaneId: runnerClaimLaneIdSchema.optional(),
1368
1780
  commandId: z.string().min(1).optional()
1369
1781
  });
1370
1782
  var activityHandoffExportSchema = z.object({
@@ -1421,6 +1833,10 @@ var projectItemUnionSchema = z.discriminatedUnion("type", [
1421
1833
  securityScanItemSchema,
1422
1834
  securityFindingItemSchema,
1423
1835
  securityPostureSnapshotItemSchema,
1836
+ testProfileItemSchema,
1837
+ testQualityScanItemSchema,
1838
+ testQualityFindingItemSchema,
1839
+ implementationTestGateItemSchema,
1424
1840
  appEvaluationScanItemSchema,
1425
1841
  appEvaluationFindingItemSchema,
1426
1842
  implementationVerificationItemSchema,
@@ -2526,6 +2942,26 @@ var ApiClient = class {
2526
2942
  }
2527
2943
  );
2528
2944
  }
2945
+ async submitTestQualityScanResult(projectId, workItemId, result) {
2946
+ return this.request(
2947
+ `/projects/${projectId}/work-items/${workItemId}/test-quality-result`,
2948
+ z3.object({ scan: testQualityScanItemSchema, findings: z3.array(testQualityFindingItemSchema), workItem: workItemSchema }),
2949
+ {
2950
+ method: "POST",
2951
+ body: JSON.stringify(result)
2952
+ }
2953
+ );
2954
+ }
2955
+ async submitImplementationTestGateResult(projectId, workItemId, result) {
2956
+ return this.request(
2957
+ `/projects/${projectId}/work-items/${workItemId}/implementation-test-gate-result`,
2958
+ z3.object({ gate: implementationTestGateItemSchema, findings: z3.array(testQualityFindingItemSchema), workItem: workItemSchema, sourceWorkItem: workItemSchema.optional() }),
2959
+ {
2960
+ method: "POST",
2961
+ body: JSON.stringify(result)
2962
+ }
2963
+ );
2964
+ }
2529
2965
  async request(urlPath, schema, init) {
2530
2966
  const response = await fetch(resolveApiUrl(this.options.apiUrl, urlPath), {
2531
2967
  ...init,
@@ -2691,6 +3127,115 @@ var brainGenerationFinalizationEntrySchema = z4.object({
2691
3127
  updatedAt: z4.string().min(1),
2692
3128
  lastError: z4.string().optional()
2693
3129
  });
3130
+ var resultFinalizationTelemetrySchema = z4.object({
3131
+ runnerId: z4.string().min(1),
3132
+ idempotencyKey: z4.string().min(1),
3133
+ tool: z4.string().min(1).optional(),
3134
+ durationMs: z4.number().int().nonnegative().optional(),
3135
+ sessionPolicy: sessionPolicySchema.optional(),
3136
+ sessionGroupKey: z4.string().min(1).optional(),
3137
+ toolSessionId: z4.string().min(1).optional(),
3138
+ sessionDecision: sessionDecisionSchema.optional(),
3139
+ sessionDecisionReason: z4.string().min(1).optional(),
3140
+ message: z4.string().optional()
3141
+ });
3142
+ var resultFinalizationFailureSchema = resultFinalizationTelemetrySchema.extend({
3143
+ status: z4.literal("failed"),
3144
+ error: z4.string().optional()
3145
+ });
3146
+ var assistantResultMutationSchema = z4.discriminatedUnion("status", [
3147
+ resultFinalizationTelemetrySchema.extend({
3148
+ status: z4.literal("completed"),
3149
+ answer: assistantAnswerResultSchema.shape.answer,
3150
+ sourceBoundary: assistantAnswerResultSchema.shape.sourceBoundary.optional(),
3151
+ citations: assistantAnswerResultSchema.shape.citations.optional()
3152
+ }),
3153
+ resultFinalizationFailureSchema
3154
+ ]);
3155
+ var impactPreviewResultMutationSchema = z4.discriminatedUnion("status", [
3156
+ resultFinalizationTelemetrySchema.extend({
3157
+ status: z4.literal("completed"),
3158
+ report: impactPreviewResultSchema
3159
+ }),
3160
+ resultFinalizationFailureSchema
3161
+ ]);
3162
+ var issueDiagnosisResultMutationSchema = z4.discriminatedUnion("status", [
3163
+ resultFinalizationTelemetrySchema.extend({
3164
+ status: z4.literal("completed"),
3165
+ diagnosis: issueDiagnosisResultSchema
3166
+ }),
3167
+ resultFinalizationFailureSchema
3168
+ ]);
3169
+ var securityPostureScanResultMutationSchema = z4.discriminatedUnion("status", [
3170
+ resultFinalizationTelemetrySchema.extend({
3171
+ status: z4.literal("completed"),
3172
+ result: securityPostureScanResultSchema
3173
+ }),
3174
+ resultFinalizationFailureSchema
3175
+ ]);
3176
+ var appEvaluationScanResultMutationSchema = z4.discriminatedUnion("status", [
3177
+ resultFinalizationTelemetrySchema.extend({
3178
+ status: z4.literal("completed"),
3179
+ result: appEvaluationScanResultSchema
3180
+ }),
3181
+ resultFinalizationFailureSchema
3182
+ ]);
3183
+ var projectContextRefreshResultMutationSchema = z4.discriminatedUnion("status", [
3184
+ resultFinalizationTelemetrySchema.extend({
3185
+ status: z4.literal("completed"),
3186
+ result: projectContextRefreshResultSchema
3187
+ }),
3188
+ resultFinalizationFailureSchema
3189
+ ]);
3190
+ var implementationVerificationResultMutationSchema = z4.discriminatedUnion("status", [
3191
+ resultFinalizationTelemetrySchema.extend({
3192
+ status: z4.literal("completed"),
3193
+ result: implementationVerificationResultSchema
3194
+ }),
3195
+ resultFinalizationFailureSchema
3196
+ ]);
3197
+ var testQualityScanResultMutationSchema = z4.discriminatedUnion("status", [
3198
+ resultFinalizationTelemetrySchema.extend({
3199
+ status: z4.literal("completed"),
3200
+ result: testQualityScanResultSchema
3201
+ }),
3202
+ resultFinalizationFailureSchema
3203
+ ]);
3204
+ var implementationTestGateResultMutationSchema = z4.discriminatedUnion("status", [
3205
+ resultFinalizationTelemetrySchema.extend({
3206
+ status: z4.literal("completed"),
3207
+ result: implementationTestGateResultSchema
3208
+ }),
3209
+ resultFinalizationFailureSchema
3210
+ ]);
3211
+ var durableResultMutationSchema = z4.discriminatedUnion("resultKind", [
3212
+ z4.object({ resultKind: z4.literal("assistantResult"), result: assistantResultMutationSchema }),
3213
+ z4.object({ resultKind: z4.literal("impactPreviewResult"), result: impactPreviewResultMutationSchema }),
3214
+ z4.object({ resultKind: z4.literal("issueDiagnosisResult"), result: issueDiagnosisResultMutationSchema }),
3215
+ z4.object({ resultKind: z4.literal("securityPostureScanResult"), result: securityPostureScanResultMutationSchema }),
3216
+ z4.object({ resultKind: z4.literal("appEvaluationScanResult"), result: appEvaluationScanResultMutationSchema }),
3217
+ z4.object({ resultKind: z4.literal("projectContextRefreshResult"), result: projectContextRefreshResultMutationSchema }),
3218
+ z4.object({ resultKind: z4.literal("implementationVerificationResult"), result: implementationVerificationResultMutationSchema }),
3219
+ z4.object({ resultKind: z4.literal("testQualityScanResult"), result: testQualityScanResultMutationSchema }),
3220
+ z4.object({ resultKind: z4.literal("implementationTestGateResult"), result: implementationTestGateResultMutationSchema })
3221
+ ]);
3222
+ var durableResultFinalizationEntrySchema = z4.object({
3223
+ schemaVersion: z4.literal(1),
3224
+ kind: z4.literal("runnerResult"),
3225
+ status: z4.enum(["pending", "terminal"]),
3226
+ accountId: z4.string().min(1),
3227
+ projectId: z4.string().min(1),
3228
+ repositoryLinkId: z4.string().min(1),
3229
+ runnerId: z4.string().min(1),
3230
+ workItemId: z4.string().min(1),
3231
+ workKind: z4.enum(["assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification", "testQualityScan", "implementationTestGate"]),
3232
+ attempt: z4.number().int().nonnegative(),
3233
+ idempotencyKey: z4.string().min(1),
3234
+ retryCount: z4.number().int().nonnegative(),
3235
+ createdAt: z4.string().min(1),
3236
+ updatedAt: z4.string().min(1),
3237
+ lastError: z4.string().optional()
3238
+ }).and(durableResultMutationSchema);
2694
3239
  function defaultResultFinalizationOutboxDir() {
2695
3240
  return path5.join(os2.homedir(), ".config", "amistio", "result-finalizations");
2696
3241
  }
@@ -2713,6 +3258,26 @@ function createBrainGenerationFinalizationEntry(input, now = (/* @__PURE__ */ ne
2713
3258
  updatedAt: now
2714
3259
  });
2715
3260
  }
3261
+ function createDurableResultFinalizationEntry(input, now = (/* @__PURE__ */ new Date()).toISOString()) {
3262
+ return durableResultFinalizationEntrySchema.parse({
3263
+ schemaVersion: 1,
3264
+ kind: "runnerResult",
3265
+ status: "pending",
3266
+ accountId: input.accountId,
3267
+ projectId: input.projectId,
3268
+ repositoryLinkId: input.repositoryLinkId,
3269
+ runnerId: input.runnerId,
3270
+ workItemId: input.workItemId,
3271
+ workKind: input.workKind,
3272
+ attempt: input.attempt,
3273
+ idempotencyKey: input.idempotencyKey,
3274
+ resultKind: input.resultKind,
3275
+ result: input.result,
3276
+ retryCount: 0,
3277
+ createdAt: now,
3278
+ updatedAt: now
3279
+ });
3280
+ }
2716
3281
  async function upsertBrainGenerationFinalizationEntry(entry, outboxDir = defaultResultFinalizationOutboxDir()) {
2717
3282
  await mkdir5(outboxDir, { recursive: true });
2718
3283
  const filePath = brainGenerationFinalizationEntryPath(entry, outboxDir);
@@ -2745,6 +3310,38 @@ async function listPendingBrainGenerationFinalizations(scope, outboxDir = defaul
2745
3310
  }
2746
3311
  return pending.sort((first, second) => Date.parse(first.createdAt) - Date.parse(second.createdAt));
2747
3312
  }
3313
+ async function upsertDurableResultFinalizationEntry(entry, outboxDir = defaultResultFinalizationOutboxDir()) {
3314
+ await mkdir5(outboxDir, { recursive: true });
3315
+ const filePath = durableResultFinalizationEntryPath(entry, outboxDir);
3316
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
3317
+ await writeFile4(tempPath, `${JSON.stringify(entry, null, 2)}
3318
+ `, "utf8");
3319
+ await rename(tempPath, filePath);
3320
+ }
3321
+ async function listPendingDurableResultFinalizations(scope, outboxDir = defaultResultFinalizationOutboxDir()) {
3322
+ const entries = await readdir2(outboxDir, { withFileTypes: true }).catch((error) => {
3323
+ if (error.code === "ENOENT") return [];
3324
+ throw error;
3325
+ });
3326
+ const pending = [];
3327
+ for (const entry of entries) {
3328
+ if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
3329
+ const filePath = path5.join(outboxDir, entry.name);
3330
+ let raw;
3331
+ try {
3332
+ raw = JSON.parse(await readFile3(filePath, "utf8"));
3333
+ } catch {
3334
+ continue;
3335
+ }
3336
+ const parsed = durableResultFinalizationEntrySchema.safeParse(raw);
3337
+ if (!parsed.success) continue;
3338
+ const value = parsed.data;
3339
+ if (value.status !== "pending") continue;
3340
+ if (value.accountId !== scope.accountId || value.projectId !== scope.projectId || value.repositoryLinkId !== scope.repositoryLinkId || value.runnerId !== scope.runnerId) continue;
3341
+ pending.push(value);
3342
+ }
3343
+ return pending.sort((first, second) => Date.parse(first.createdAt) - Date.parse(second.createdAt));
3344
+ }
2748
3345
  async function markBrainGenerationFinalizationRetry(entry, error, outboxDir = defaultResultFinalizationOutboxDir()) {
2749
3346
  const updated = brainGenerationFinalizationEntrySchema.parse({
2750
3347
  ...entry,
@@ -2769,6 +3366,30 @@ async function markBrainGenerationFinalizationTerminal(entry, error, outboxDir =
2769
3366
  async function deleteBrainGenerationFinalizationEntry(entry, outboxDir = defaultResultFinalizationOutboxDir()) {
2770
3367
  await rm(brainGenerationFinalizationEntryPath(entry, outboxDir), { force: true });
2771
3368
  }
3369
+ async function markDurableResultFinalizationRetry(entry, error, outboxDir = defaultResultFinalizationOutboxDir()) {
3370
+ const updated = durableResultFinalizationEntrySchema.parse({
3371
+ ...entry,
3372
+ status: "pending",
3373
+ retryCount: entry.retryCount + 1,
3374
+ lastError: truncateLocalError(error),
3375
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3376
+ });
3377
+ await upsertDurableResultFinalizationEntry(updated, outboxDir);
3378
+ return updated;
3379
+ }
3380
+ async function markDurableResultFinalizationTerminal(entry, error, outboxDir = defaultResultFinalizationOutboxDir()) {
3381
+ const updated = durableResultFinalizationEntrySchema.parse({
3382
+ ...entry,
3383
+ status: "terminal",
3384
+ lastError: truncateLocalError(error),
3385
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3386
+ });
3387
+ await upsertDurableResultFinalizationEntry(updated, outboxDir);
3388
+ return updated;
3389
+ }
3390
+ async function deleteDurableResultFinalizationEntry(entry, outboxDir = defaultResultFinalizationOutboxDir()) {
3391
+ await rm(durableResultFinalizationEntryPath(entry, outboxDir), { force: true });
3392
+ }
2772
3393
  function brainGenerationFinalizationEntryPath(entry, outboxDir) {
2773
3394
  return path5.join(outboxDir, `${brainGenerationFinalizationEntryKey(entry)}.json`);
2774
3395
  }
@@ -2776,6 +3397,13 @@ function brainGenerationFinalizationEntryKey(entry) {
2776
3397
  const hash = createHash2("sha256").update([entry.accountId, entry.projectId, entry.repositoryLinkId, entry.runnerId, entry.workItemId, String(entry.attempt), entry.idempotencyKey].join("\0")).digest("hex").slice(0, 32);
2777
3398
  return `brain-generation-${hash}`;
2778
3399
  }
3400
+ function durableResultFinalizationEntryPath(entry, outboxDir) {
3401
+ return path5.join(outboxDir, `${durableResultFinalizationEntryKey(entry)}.json`);
3402
+ }
3403
+ function durableResultFinalizationEntryKey(entry) {
3404
+ const hash = createHash2("sha256").update([entry.accountId, entry.projectId, entry.repositoryLinkId, entry.runnerId, entry.workItemId, String(entry.attempt), entry.idempotencyKey, entry.resultKind].join("\0")).digest("hex").slice(0, 32);
3405
+ return `runner-result-${hash}`;
3406
+ }
2779
3407
  function truncateLocalError(error) {
2780
3408
  const trimmed = error.trim();
2781
3409
  return trimmed.length > 600 ? `${trimmed.slice(0, 600)}...` : trimmed;
@@ -4725,6 +5353,10 @@ var projectContextRefreshStart = "AMISTIO_PROJECT_CONTEXT_REFRESH_START";
4725
5353
  var projectContextRefreshEnd = "AMISTIO_PROJECT_CONTEXT_REFRESH_END";
4726
5354
  var implementationVerificationStart = "AMISTIO_IMPLEMENTATION_VERIFICATION_START";
4727
5355
  var implementationVerificationEnd = "AMISTIO_IMPLEMENTATION_VERIFICATION_END";
5356
+ var testQualityStart = "AMISTIO_TEST_QUALITY_START";
5357
+ var testQualityEnd = "AMISTIO_TEST_QUALITY_END";
5358
+ var implementationTestGateStart = "AMISTIO_IMPLEMENTATION_TEST_GATE_START";
5359
+ var implementationTestGateEnd = "AMISTIO_IMPLEMENTATION_TEST_GATE_END";
4728
5360
  var projectContextMissingAreaMaxLength = 160;
4729
5361
  var projectContextCoverageWarningMaxLength = 300;
4730
5362
  var projectContextVerificationPlanMaxLength = 300;
@@ -4732,6 +5364,94 @@ var projectContextTopLevelWarningMaxLength = 600;
4732
5364
  var validProjectContextSliceKinds = /* @__PURE__ */ new Set(["overview", "architecture", "domain", "data", "api", "frontend", "backend", "cli", "workflow", "operations", "security", "testing", "unknown"]);
4733
5365
  var validProjectContextEntityTypes = /* @__PURE__ */ new Set(["project", "system", "component", "domain", "tool", "decision", "feature", "risk", "team", "workflow", "unknown"]);
4734
5366
  var validProjectContextRelationTypes = /* @__PURE__ */ new Set(["uses", "depends_on", "decides", "supersedes", "touches", "blocks", "implements", "mentions"]);
5367
+ var canonicalAppEvaluationCategories = /* @__PURE__ */ new Map([
5368
+ ["verification", "verification"],
5369
+ ["verify", "verification"],
5370
+ ["test", "verification"],
5371
+ ["tests", "verification"],
5372
+ ["testing", "verification"],
5373
+ ["check", "verification"],
5374
+ ["checks", "verification"],
5375
+ ["ci", "verification"],
5376
+ ["continuousintegration", "verification"],
5377
+ ["qa", "verification"],
5378
+ ["qualityassurance", "verification"],
5379
+ ["build", "verification"],
5380
+ ["lint", "verification"],
5381
+ ["linting", "verification"],
5382
+ ["typecheck", "verification"],
5383
+ ["typechecking", "verification"],
5384
+ ["productdrift", "productDrift"],
5385
+ ["drift", "productDrift"],
5386
+ ["docs", "productDrift"],
5387
+ ["doc", "productDrift"],
5388
+ ["documentation", "productDrift"],
5389
+ ["docsdrift", "productDrift"],
5390
+ ["documentationdrift", "productDrift"],
5391
+ ["contextdrift", "productDrift"],
5392
+ ["productdocumentationdrift", "productDrift"],
5393
+ ["plancleanup", "planCleanup"],
5394
+ ["plan", "planCleanup"],
5395
+ ["plans", "planCleanup"],
5396
+ ["staleplan", "planCleanup"],
5397
+ ["staleplans", "planCleanup"],
5398
+ ["cleanup", "planCleanup"],
5399
+ ["cleanupplan", "planCleanup"],
5400
+ ["superseded", "planCleanup"],
5401
+ ["obsolete", "planCleanup"],
5402
+ ["promptrot", "promptRot"],
5403
+ ["prompt", "promptRot"],
5404
+ ["prompts", "promptRot"],
5405
+ ["template", "promptRot"],
5406
+ ["templates", "promptRot"],
5407
+ ["instruction", "promptRot"],
5408
+ ["instructions", "promptRot"],
5409
+ ["runnerprompt", "promptRot"],
5410
+ ["runnerprompttemplate", "promptRot"],
5411
+ ["prompttemplate", "promptRot"],
5412
+ ["missingmemory", "missingMemory"],
5413
+ ["memory", "missingMemory"],
5414
+ ["memories", "missingMemory"],
5415
+ ["lesson", "missingMemory"],
5416
+ ["lessons", "missingMemory"],
5417
+ ["workflow", "missingMemory"],
5418
+ ["workflowlesson", "missingMemory"],
5419
+ ["workflows", "missingMemory"],
5420
+ ["releasereadiness", "releaseReadiness"],
5421
+ ["release", "releaseReadiness"],
5422
+ ["deploy", "releaseReadiness"],
5423
+ ["deployment", "releaseReadiness"],
5424
+ ["readiness", "releaseReadiness"],
5425
+ ["production", "releaseReadiness"],
5426
+ ["productionreadiness", "releaseReadiness"],
5427
+ ["ux", "ux"],
5428
+ ["ui", "ux"],
5429
+ ["userexperience", "ux"],
5430
+ ["accessibility", "accessibility"],
5431
+ ["a11y", "accessibility"],
5432
+ ["accessible", "accessibility"],
5433
+ ["performance", "performance"],
5434
+ ["perf", "performance"],
5435
+ ["reliability", "reliability"],
5436
+ ["resilience", "reliability"],
5437
+ ["resiliency", "reliability"],
5438
+ ["stability", "reliability"],
5439
+ ["uptime", "reliability"],
5440
+ ["securityposture", "securityPosture"],
5441
+ ["security", "securityPosture"],
5442
+ ["posture", "securityPosture"],
5443
+ ["vulnerability", "securityPosture"],
5444
+ ["vulnerabilities", "securityPosture"],
5445
+ ["vulnerabilityposture", "securityPosture"],
5446
+ ["other", "other"],
5447
+ ["technicaldebt", "other"],
5448
+ ["debt", "other"],
5449
+ ["maintainability", "other"],
5450
+ ["codequality", "other"],
5451
+ ["architecture", "other"],
5452
+ ["operations", "other"],
5453
+ ["ops", "other"]
5454
+ ]);
4735
5455
  var canonicalProjectContextCitationSources = /* @__PURE__ */ new Map([
4736
5456
  ["projectbrain", "projectBrain"],
4737
5457
  ["approvedbrain", "projectBrain"],
@@ -4756,6 +5476,7 @@ function createImplementationVerificationPrompt(workItem) {
4756
5476
  `Project ID: ${workItem.projectId}`,
4757
5477
  `Implementation verification ID: ${workItem.implementationVerificationId ?? "unknown"}`,
4758
5478
  `Source work item ID: ${workItem.sourceWorkItemId ?? "unknown"}`,
5479
+ ...autopilotPromptLines(workItem),
4759
5480
  "",
4760
5481
  "## Verification Request",
4761
5482
  "",
@@ -4789,23 +5510,115 @@ function createImplementationVerificationPrompt(workItem) {
4789
5510
  "Do not put Markdown fences around the markers. Do not implement or fix anything during this verification pass."
4790
5511
  ].join("\n");
4791
5512
  }
4792
- function createWorkExecutionPrompt(workItem, context) {
4793
- if (workItem.workKind === "brainGeneration") {
4794
- return createBrainGenerationPrompt(workItem);
4795
- }
4796
- if (workItem.workKind === "planRevision") {
4797
- return createPlanRevisionPrompt(workItem, context?.planRevision);
4798
- }
4799
- if (workItem.workKind === "assistantQuestion") {
4800
- return createAssistantQuestionPrompt(workItem, context?.assistantQuestion);
4801
- }
4802
- if (workItem.workKind === "impactPreview") {
4803
- return createImpactPreviewPrompt(workItem, context?.impactPreview);
4804
- }
4805
- if (workItem.workKind === "issueDiagnosis") {
4806
- return createIssueDiagnosisPrompt(workItem, context?.issueDiagnosis);
4807
- }
4808
- if (workItem.workKind === "securityPostureScan") {
5513
+ function createTestQualityScanPrompt(workItem, context) {
5514
+ const approvedContext = (context?.documents ?? []).filter((document) => document.status === "approved" || document.syncState === "approved" || document.syncState === "synced").slice(0, 20).map((document) => [
5515
+ `### ${document.title}`,
5516
+ `documentId: ${document.documentId}`,
5517
+ `documentType: ${document.documentType}`,
5518
+ `repoPath: ${document.repoPath}`,
5519
+ `revision: ${document.revision}`,
5520
+ document.content.slice(0, 3e3)
5521
+ ].join("\n")).join("\n\n");
5522
+ return [
5523
+ "# Amistio Test Quality Scan",
5524
+ "",
5525
+ "You are running locally through the Amistio CLI inside the user's repository.",
5526
+ "Run a non-mutating daily test, coverage, and quality scan for this repository.",
5527
+ "Do not modify files, create branches, commit, push, install packages, run implementation prompts, or make unaudited network calls.",
5528
+ "Use safe local metadata to infer package managers and scripts, then run existing lint, typecheck, test, coverage, and build commands only when they already exist.",
5529
+ "Return missing tests, missing coverage, low coverage, failing quality commands, and broken whole-app verification as reviewable findings that can become plans.",
5530
+ "",
5531
+ "## Work Item",
5532
+ "",
5533
+ `Title: ${workItem.title}`,
5534
+ `Work item ID: ${workItem.workItemId}`,
5535
+ `Project ID: ${workItem.projectId}`,
5536
+ `Test quality scan ID: ${workItem.testQualityScanId ?? "unknown"}`,
5537
+ "",
5538
+ "## Approved Project Brain Context",
5539
+ "",
5540
+ approvedContext || "No approved project-brain records were loaded. Inspect safe local metadata and summarize the context gap.",
5541
+ "",
5542
+ "## Requirements",
5543
+ "",
5544
+ "- Prefer the repository whole-app verification script when one exists, then focused package scripts.",
5545
+ "- Detect package managers from lockfiles and package metadata without uploading raw source.",
5546
+ "- Capture bounded summaries, exit codes, coverage metrics, and repository-relative safe paths only.",
5547
+ "- If coverage cannot be measured, return a missingCoverage finding instead of guessing.",
5548
+ "- If tests are missing or impossible to run, return a missingTests or testGap finding with a concrete plan and verification steps.",
5549
+ "- Redact secrets, absolute paths, env vars, process lists, tokens, provider sessions, and command lines containing secret-looking values.",
5550
+ "",
5551
+ "## Output Contract",
5552
+ "",
5553
+ "Print exactly one JSON object between the markers below. The CLI will submit only this structured test scan result back to Amistio.",
5554
+ "Accepted command kind values: lint, typecheck, unitTest, integrationTest, e2eTest, coverage, build, verify, other.",
5555
+ "Accepted command status values: passed, failed, skipped, missing, blocked.",
5556
+ "Accepted finding categories: missingTests, missingCoverage, lowCoverage, failingTests, failingQuality, missingCommand, flakyTests, unverifiedImplementation, testGap, other.",
5557
+ "",
5558
+ testQualityStart,
5559
+ '{"summary":"Whole-app verification exists, but coverage reporting is missing for one package.","profile":{"testProfileId":"test_profile_detected","repositoryLinkId":"repository_link_placeholder","status":"detected","enabled":true,"detectedPackageManager":"pnpm","packageManagers":["pnpm"],"commands":[{"commandId":"verify","kind":"verify","label":"Whole-app verification","source":"detected","scriptName":"verify","workingDirectory":".","required":true}],"coverageThresholds":{"lines":80},"defaultWholeAppCommandId":"verify","focusedCommandIds":[],"lastDetectedAt":"2026-01-01T00:00:00.000Z"},"commandSummaries":[{"commandId":"verify","kind":"verify","label":"Whole-app verification","status":"passed","exitCode":0,"summary":"The repository verification script completed successfully.","safePaths":["package.json"]}],"coverage":{"status":"missing","thresholds":{"lines":80},"summary":"No coverage report was found."},"findings":[{"title":"Coverage report is missing","category":"missingCoverage","severity":"medium","confidence":"high","summary":"The repository test profile does not produce a coverage summary.","affectedSurfaces":["Test verification"],"evidence":["The scan found a test command but no coverage output."],"safePaths":["package.json"],"suggestedAction":"Add or document a coverage command for the affected package and include it in whole-app verification.","verificationPlan":["Run the new coverage command locally","Confirm coverage appears in the Test panel"],"dedupeKey":"missing-coverage"}],"blockedReasons":[],"redactionState":{"status":"clean","redactedFields":[]},"verificationPlan":["Review generated Test findings","Run the whole-app verification command before implementation handoff"],"warnings":[]}',
5560
+ testQualityEnd,
5561
+ "",
5562
+ "Do not put Markdown fences around the markers. Do not implement fixes during this scan."
5563
+ ].join("\n");
5564
+ }
5565
+ function createImplementationTestGatePrompt(workItem) {
5566
+ return [
5567
+ "# Amistio Implementation Test Gate",
5568
+ "",
5569
+ "You are running locally through the Amistio CLI inside the user's repository.",
5570
+ "Run the required test gate before implementation completion, PR handoff, or runner-managed push.",
5571
+ "Do not modify files, install packages unless an already-approved preflight policy permits it, commit, push, or upload raw source.",
5572
+ "Run focused checks for the touched scope first when available, then the whole-app verification command when practical.",
5573
+ "If tests are missing or impossible, return blocked with a test-gap justification and a finding instead of marking the gate passed.",
5574
+ "",
5575
+ "## Work Item",
5576
+ "",
5577
+ `Title: ${workItem.title}`,
5578
+ `Work item ID: ${workItem.workItemId}`,
5579
+ `Project ID: ${workItem.projectId}`,
5580
+ `Implementation test gate ID: ${workItem.implementationTestGateId ?? "unknown"}`,
5581
+ `Source work item ID: ${workItem.sourceWorkItemId ?? "unknown"}`,
5582
+ "",
5583
+ "## Gate Request",
5584
+ "",
5585
+ workItem.sourceWish ?? "Run focused checks and whole-app verification before marking implementation complete.",
5586
+ "",
5587
+ "## Data Safety",
5588
+ "",
5589
+ "- Persist only bounded summaries, coverage metrics, exit codes, and repository-relative paths.",
5590
+ "- Do not include raw source, secrets, env vars, process lists, absolute local paths, credential values, tokens, provider sessions, or destructive shell output.",
5591
+ "- Use safePaths for repository-relative paths only; never include /absolute paths or ../ traversal.",
5592
+ "",
5593
+ "## Output Contract",
5594
+ "",
5595
+ "Print exactly one JSON object between the markers below. The CLI will submit only this structured gate result back to Amistio.",
5596
+ "Accepted outcome values: passed, failed, blocked, overridden.",
5597
+ "",
5598
+ implementationTestGateStart,
5599
+ '{"outcome":"passed","summary":"Focused checks and whole-app verification passed.","commandSummaries":[{"commandId":"verify","kind":"verify","label":"Whole-app verification","status":"passed","exitCode":0,"summary":"The repository verification script completed successfully.","safePaths":["package.json"]}],"coverage":{"status":"unknown","thresholds":{},"summary":"No coverage threshold was configured for this gate."},"findings":[],"blockedReasons":[],"redactionState":{"status":"clean","redactedFields":[]},"verificationPlan":["Record this gate result before marking implementation complete"],"warnings":[]}',
5600
+ implementationTestGateEnd,
5601
+ "",
5602
+ "Do not put Markdown fences around the markers."
5603
+ ].join("\n");
5604
+ }
5605
+ function createWorkExecutionPrompt(workItem, context) {
5606
+ if (workItem.workKind === "brainGeneration") {
5607
+ return createBrainGenerationPrompt(workItem);
5608
+ }
5609
+ if (workItem.workKind === "planRevision") {
5610
+ return createPlanRevisionPrompt(workItem, context?.planRevision);
5611
+ }
5612
+ if (workItem.workKind === "assistantQuestion") {
5613
+ return createAssistantQuestionPrompt(workItem, context?.assistantQuestion);
5614
+ }
5615
+ if (workItem.workKind === "impactPreview") {
5616
+ return createImpactPreviewPrompt(workItem, context?.impactPreview);
5617
+ }
5618
+ if (workItem.workKind === "issueDiagnosis") {
5619
+ return createIssueDiagnosisPrompt(workItem, context?.issueDiagnosis);
5620
+ }
5621
+ if (workItem.workKind === "securityPostureScan") {
4809
5622
  return createSecurityPostureScanPrompt(workItem, context?.securityPostureScan);
4810
5623
  }
4811
5624
  if (workItem.workKind === "appEvaluationScan") {
@@ -4817,6 +5630,12 @@ function createWorkExecutionPrompt(workItem, context) {
4817
5630
  if (workItem.workKind === "implementationVerification") {
4818
5631
  return createImplementationVerificationPrompt(workItem);
4819
5632
  }
5633
+ if (workItem.workKind === "testQualityScan") {
5634
+ return createTestQualityScanPrompt(workItem, context?.testQualityScan);
5635
+ }
5636
+ if (workItem.workKind === "implementationTestGate") {
5637
+ return createImplementationTestGatePrompt(workItem);
5638
+ }
4820
5639
  return [
4821
5640
  "# Amistio Work Execution",
4822
5641
  "",
@@ -4831,6 +5650,7 @@ function createWorkExecutionPrompt(workItem, context) {
4831
5650
  `Implementation scope: ${workItem.implementationScopeId ?? workItem.controllingAdrId ?? "work-item"}`,
4832
5651
  `Execution branch: ${workItem.executionBranch ?? "managed by Amistio CLI"}`,
4833
5652
  `Execution worktree key: ${workItem.executionWorktreeKey ?? "managed by Amistio CLI"}`,
5653
+ ...autopilotPromptLines(workItem),
4834
5654
  "",
4835
5655
  "## Rules",
4836
5656
  "",
@@ -4845,6 +5665,27 @@ function createWorkExecutionPrompt(workItem, context) {
4845
5665
  "- Run relevant verification commands when feasible and summarize results."
4846
5666
  ].join("\n");
4847
5667
  }
5668
+ function autopilotPromptLines(workItem) {
5669
+ const autopilot = autopilotWorkMetadata(workItem);
5670
+ if (!autopilot.autopilotAuthorizationId) {
5671
+ return [];
5672
+ }
5673
+ const lines = [
5674
+ `Autopilot authorization ID: ${autopilot.autopilotAuthorizationId}`,
5675
+ ...autopilot.autopilotCandidateId ? [`Autopilot candidate ID: ${autopilot.autopilotCandidateId}`] : [],
5676
+ ...autopilot.autopilotCandidateType ? [`Autopilot candidate type: ${autopilot.autopilotCandidateType}`] : [],
5677
+ `Autopilot outcome: ${autopilot.autopilotClassificationOutcome ?? "unknown"}`,
5678
+ `Autopilot policy: ${autopilot.autopilotPolicyVersion ?? "unknown"}`
5679
+ ];
5680
+ if (autopilot.autopilotAuthorization?.summary) {
5681
+ lines.push(`Autopilot summary: ${autopilot.autopilotAuthorization.summary}`);
5682
+ }
5683
+ lines.push("Autopilot satisfies the app approval gate only for the audited low-risk work item; it does not loosen local repository, credential, or data-safety boundaries.");
5684
+ return lines;
5685
+ }
5686
+ function autopilotWorkMetadata(workItem) {
5687
+ return workItem;
5688
+ }
4848
5689
  function createProjectContextRefreshPrompt(workItem, context) {
4849
5690
  const approvedContext = (context?.documents ?? []).filter((document) => document.status === "approved" || document.syncState === "approved" || document.syncState === "synced").slice(0, 24).map((document) => [
4850
5691
  `### ${document.title}`,
@@ -5016,6 +5857,8 @@ function createAppEvaluationScanPrompt(workItem, context) {
5016
5857
  "- Check app verification health: lint, typecheck, test, build, CI references, flaky or missing gates.",
5017
5858
  "- Check product and docs drift between approved context, plans, prompts, and current implementation.",
5018
5859
  "- Check old plans and prompts for cleanup candidates, but only propose lifecycle actions such as markCompleted, markSuperseded, archive, keepActive, or none.",
5860
+ "- Treat active proposed, approved, ready, or in-progress plans/prompts with accepted controlling ADRs, features, or work artifacts as active work. Do not classify them as cleanup material solely because they are older or not yet fully implemented; use keepActive when they still describe valid pending or approved work.",
5861
+ "- When lifecycle metadata disagrees across indexes, frontmatter, feature specs, ADRs, and implementation evidence, cite the conflict and propose a metadata correction or verification step instead of archival/removal.",
5019
5862
  "- Check missing memory or workflow updates when repeated lessons or operational rules are visible.",
5020
5863
  "- Check release readiness, UX, accessibility, performance, reliability, and security-posture follow-through at a summary level.",
5021
5864
  "",
@@ -5028,6 +5871,10 @@ function createAppEvaluationScanPrompt(workItem, context) {
5028
5871
  "## Output Contract",
5029
5872
  "",
5030
5873
  "Print exactly one JSON object between the markers below. The CLI will submit only this structured scan result back to Amistio.",
5874
+ "Accepted category values: verification, productDrift, planCleanup, promptRot, missingMemory, releaseReadiness, ux, accessibility, performance, reliability, securityPosture, other.",
5875
+ "Accepted severity values: info, low, medium, high, critical.",
5876
+ "Accepted confidence values: low, medium, high.",
5877
+ "Accepted proposedLifecycleAction values: createPlan, updatePlan, markCompleted, markImplemented, markSuperseded, markBlocked, archive, keepActive, none.",
5031
5878
  "",
5032
5879
  appEvaluationStart,
5033
5880
  '{"summary":"The app is generally healthy, but one release-readiness plan needs cleanup and verification evidence should be refreshed.","baselineVersion":"amistio-app-evaluation-v1","findings":[{"title":"Stale release plan should be marked superseded","category":"planCleanup","severity":"low","confidence":"high","summary":"A release plan appears to describe a workflow that has since been replaced by a newer accepted plan.","affectedSurfaces":["docs/plans"],"evidence":["A newer plan references the same scope and status, while the older plan remains proposed."],"suggestedAction":"Prepare a cleanup update that marks the stale plan superseded and links to the newer plan.","verificationPlan":["Review both plan files","Confirm the newer plan is accepted before changing lifecycle state"],"safePaths":["docs/plans/PLAN-example.md"],"proposedLifecycleAction":"markSuperseded","relatedArtifactIds":[]}],"verificationPlan":["Review App Evaluation findings for approval","Run the canonical verify gate before implementing approved actions"]}',
@@ -5358,7 +6205,8 @@ function parseAppEvaluationScanResult(output) {
5358
6205
  }
5359
6206
  const payload = output.slice(start + appEvaluationStart.length, end).trim();
5360
6207
  const parsed = JSON.parse(stripJsonFence(payload));
5361
- return appEvaluationScanResultSchema.parse(parsed);
6208
+ const normalized = normalizeAppEvaluationScanResultPaths(normalizeAppEvaluationScanResultEnums(parsed));
6209
+ return appEvaluationScanResultSchema.parse(normalized);
5362
6210
  }
5363
6211
  function parseProjectContextRefreshResult(output, options = {}) {
5364
6212
  const start = output.indexOf(projectContextRefreshStart);
@@ -5381,6 +6229,26 @@ function parseImplementationVerificationResult(output) {
5381
6229
  const parsed = JSON.parse(stripJsonFence(payload));
5382
6230
  return implementationVerificationResultSchema.parse(parsed);
5383
6231
  }
6232
+ function parseTestQualityScanResult(output) {
6233
+ const start = output.indexOf(testQualityStart);
6234
+ const end = output.indexOf(testQualityEnd, start + testQualityStart.length);
6235
+ if (start === -1 || end === -1 || end <= start) {
6236
+ throw new Error("Local AI scan did not return an Amistio test quality block.");
6237
+ }
6238
+ const payload = output.slice(start + testQualityStart.length, end).trim();
6239
+ const parsed = JSON.parse(stripJsonFence(payload));
6240
+ return testQualityScanResultSchema.parse(parsed);
6241
+ }
6242
+ function parseImplementationTestGateResult(output) {
6243
+ const start = output.indexOf(implementationTestGateStart);
6244
+ const end = output.indexOf(implementationTestGateEnd, start + implementationTestGateStart.length);
6245
+ if (start === -1 || end === -1 || end <= start) {
6246
+ throw new Error("Local AI gate did not return an Amistio implementation test gate block.");
6247
+ }
6248
+ const payload = output.slice(start + implementationTestGateStart.length, end).trim();
6249
+ const parsed = JSON.parse(stripJsonFence(payload));
6250
+ return implementationTestGateResultSchema.parse(parsed);
6251
+ }
5384
6252
  function projectContextRefreshSubmissionFailureSummary(result) {
5385
6253
  if (result.refresh.status !== "failed" && result.workItem.status !== "failed") {
5386
6254
  return void 0;
@@ -5482,6 +6350,75 @@ function normalizeProjectContextRefreshEnums(value) {
5482
6350
  }
5483
6351
  return normalized;
5484
6352
  }
6353
+ function normalizeAppEvaluationScanResultEnums(value) {
6354
+ if (!isObjectRecord(value)) {
6355
+ return value;
6356
+ }
6357
+ const normalized = { ...value };
6358
+ if (Array.isArray(normalized.findings)) {
6359
+ normalized.findings = normalized.findings.map((finding) => {
6360
+ if (!isObjectRecord(finding)) {
6361
+ return finding;
6362
+ }
6363
+ const normalizedFinding = { ...finding };
6364
+ normalizedFinding.category = normalizeAppEvaluationFindingCategory(finding.category);
6365
+ return normalizedFinding;
6366
+ });
6367
+ }
6368
+ return normalized;
6369
+ }
6370
+ function normalizeAppEvaluationFindingCategory(value) {
6371
+ if (typeof value !== "string") {
6372
+ return value;
6373
+ }
6374
+ const trimmed = value.trim();
6375
+ if (!trimmed) {
6376
+ return "other";
6377
+ }
6378
+ const direct = canonicalAppEvaluationCategories.get(normalizeEnumKey(trimmed));
6379
+ if (direct) {
6380
+ return direct;
6381
+ }
6382
+ for (const segment of trimmed.split(/[\/,|]+/)) {
6383
+ const segmentCategory = canonicalAppEvaluationCategories.get(normalizeEnumKey(segment));
6384
+ if (segmentCategory) {
6385
+ return segmentCategory;
6386
+ }
6387
+ }
6388
+ return "other";
6389
+ }
6390
+ function normalizeEnumKey(value) {
6391
+ return value.trim().replace(/[^A-Za-z0-9]+/g, "").toLowerCase();
6392
+ }
6393
+ function normalizeAppEvaluationScanResultPaths(value) {
6394
+ if (!isObjectRecord(value)) {
6395
+ return value;
6396
+ }
6397
+ const normalized = { ...value };
6398
+ if (Array.isArray(normalized.findings)) {
6399
+ normalized.findings = normalized.findings.map((finding) => {
6400
+ if (!isObjectRecord(finding)) {
6401
+ return finding;
6402
+ }
6403
+ const normalizedFinding = { ...finding };
6404
+ if (Array.isArray(normalizedFinding.safePaths)) {
6405
+ normalizedFinding.safePaths = normalizedFinding.safePaths.map((safePath) => typeof safePath === "string" ? normalizeAppEvaluationRepoPath(safePath) : safePath);
6406
+ }
6407
+ if (typeof normalizedFinding.proposedPlanRepoPath === "string") {
6408
+ normalizedFinding.proposedPlanRepoPath = normalizeAppEvaluationRepoPath(normalizedFinding.proposedPlanRepoPath);
6409
+ }
6410
+ return normalizedFinding;
6411
+ });
6412
+ }
6413
+ return normalized;
6414
+ }
6415
+ function normalizeAppEvaluationRepoPath(value) {
6416
+ try {
6417
+ return normalizeRelativeProjectContextPath(value);
6418
+ } catch {
6419
+ throw new Error("App evaluation scan contains an unsafe repository path.");
6420
+ }
6421
+ }
5485
6422
  function normalizeProjectContextSliceKind(value) {
5486
6423
  if (typeof value !== "string") {
5487
6424
  return "unknown";
@@ -6188,6 +7125,7 @@ function buildBackgroundRunnerArgs(options) {
6188
7125
  if (options.maxIterations !== void 0) {
6189
7126
  args.push("--max-iterations", String(options.maxIterations));
6190
7127
  }
7128
+ args.push("--max-concurrent-work", String(options.maxConcurrentWork));
6191
7129
  args.push("--max-preflight-attempts", String(options.maxPreflightAttempts));
6192
7130
  args.push("--tool-timeout-seconds", String(options.toolTimeoutSeconds));
6193
7131
  if (!options.stream) {
@@ -6271,10 +7209,12 @@ function truncateProcessOutput(value) {
6271
7209
 
6272
7210
  // src/git-worktree.ts
6273
7211
  import { execFile as execFile5 } from "node:child_process";
6274
- import { mkdir as mkdir10, stat as stat5 } from "node:fs/promises";
7212
+ import { copyFile, lstat, mkdir as mkdir10, readdir as readdir6, stat as stat5 } from "node:fs/promises";
6275
7213
  import path14 from "node:path";
6276
7214
  import { promisify as promisify5 } from "node:util";
6277
7215
  var execFileAsync5 = promisify5(execFile5);
7216
+ var exactLocalEnvironmentFiles = /* @__PURE__ */ new Set([".env", ".env.local", ".env.development", ".env.development.local", ".env.test", ".env.test.local", ".env.production", ".env.production.local"]);
7217
+ var localEnvironmentFilePattern = /^\.env\.[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*\.local$/;
6278
7218
  function needsGitWorktreeIsolation(workItem) {
6279
7219
  return (workItem.workKind ?? "implementation") === "implementation";
6280
7220
  }
@@ -6299,7 +7239,8 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
6299
7239
  const worktreePath = localWorktreePath(repoRoot, identity.worktreeKey);
6300
7240
  if (await pathExists(worktreePath)) {
6301
7241
  await assertExistingWorktree(worktreePath, identity.branch);
6302
- return { ...identity, baseRevision, worktreePath };
7242
+ const preparedLocalEnvironmentFileCount2 = await prepareLocalWorktreeEnvironment(repoRoot, worktreePath);
7243
+ return { ...identity, baseRevision, worktreePath, ...preparedLocalEnvironmentFileCount2 ? { preparedLocalEnvironmentFileCount: preparedLocalEnvironmentFileCount2 } : {} };
6303
7244
  }
6304
7245
  await mkdir10(path14.dirname(worktreePath), { recursive: true });
6305
7246
  const branchExists = await gitCommandSucceeds(repoRoot, ["show-ref", "--verify", "--quiet", `refs/heads/${identity.branch}`]);
@@ -6307,7 +7248,8 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
6307
7248
  await gitOutput(repoRoot, worktreeArgs).catch((error) => {
6308
7249
  throw new Error(`Could not create Git worktree ${identity.worktreeKey} on ${identity.branch}: ${errorMessage2(error)}`);
6309
7250
  });
6310
- return { ...identity, baseRevision, worktreePath };
7251
+ const preparedLocalEnvironmentFileCount = await prepareLocalWorktreeEnvironment(repoRoot, worktreePath);
7252
+ return { ...identity, baseRevision, worktreePath, ...preparedLocalEnvironmentFileCount ? { preparedLocalEnvironmentFileCount } : {} };
6311
7253
  }
6312
7254
  function localWorktreePath(repoRoot, worktreeKey) {
6313
7255
  const repoName = path14.basename(repoRoot);
@@ -6334,6 +7276,55 @@ async function assertBaseRevision(repoRoot, baseRevision, currentHead) {
6334
7276
  throw new Error(`Work item base revision ${baseRevision} is not an ancestor of ${currentHead}; refresh the work item before implementation.`);
6335
7277
  }
6336
7278
  }
7279
+ async function prepareLocalWorktreeEnvironment(repoRoot, worktreePath) {
7280
+ const candidates = await localEnvironmentFileCandidates(repoRoot);
7281
+ let preparedCount = 0;
7282
+ for (const candidate of candidates) {
7283
+ const sourcePath = path14.join(repoRoot, candidate);
7284
+ const targetPath = path14.join(worktreePath, candidate);
7285
+ if (await pathExists(targetPath)) {
7286
+ continue;
7287
+ }
7288
+ if (await gitCommandSucceeds(repoRoot, ["ls-files", "--error-unmatch", "--", candidate])) {
7289
+ continue;
7290
+ }
7291
+ if (!await gitCommandSucceeds(worktreePath, ["check-ignore", "--quiet", "--", candidate])) {
7292
+ continue;
7293
+ }
7294
+ try {
7295
+ await copyFile(sourcePath, targetPath);
7296
+ preparedCount += 1;
7297
+ } catch (error) {
7298
+ throw new Error(`Could not prepare local worktree environment files: ${safeFileError(error)}`);
7299
+ }
7300
+ }
7301
+ return preparedCount;
7302
+ }
7303
+ async function localEnvironmentFileCandidates(repoRoot) {
7304
+ const names = new Set(exactLocalEnvironmentFiles);
7305
+ for (const entry of await readdir6(repoRoot)) {
7306
+ if (isAllowedLocalEnvironmentFile(entry)) {
7307
+ names.add(entry);
7308
+ }
7309
+ }
7310
+ const candidates = [];
7311
+ for (const name of [...names].sort()) {
7312
+ if (!isRootFileName(name)) {
7313
+ continue;
7314
+ }
7315
+ const source = await lstat(path14.join(repoRoot, name)).catch(() => void 0);
7316
+ if (source?.isFile()) {
7317
+ candidates.push(name);
7318
+ }
7319
+ }
7320
+ return candidates;
7321
+ }
7322
+ function isAllowedLocalEnvironmentFile(name) {
7323
+ return exactLocalEnvironmentFiles.has(name) || localEnvironmentFilePattern.test(name);
7324
+ }
7325
+ function isRootFileName(name) {
7326
+ return name === path14.basename(name) && !name.includes("/") && !name.includes("\\");
7327
+ }
6337
7328
  async function gitOutput(cwd, args) {
6338
7329
  const { stdout } = await execFileAsync5("git", args, { cwd, maxBuffer: 1024 * 1024 });
6339
7330
  return stdout.trim();
@@ -6356,6 +7347,12 @@ function slugify(value) {
6356
7347
  function errorMessage2(error) {
6357
7348
  return error instanceof Error ? error.message : String(error);
6358
7349
  }
7350
+ function safeFileError(error) {
7351
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") {
7352
+ return error.code;
7353
+ }
7354
+ return error instanceof Error ? error.name : "unknown file error";
7355
+ }
6359
7356
 
6360
7357
  // src/implementation-handoff.ts
6361
7358
  import { execFile as execFile6 } from "node:child_process";
@@ -6375,9 +7372,17 @@ async function completeImplementationHandoff(input) {
6375
7372
  headBranch
6376
7373
  };
6377
7374
  try {
7375
+ const artifactResult = await materializeApprovedArtifacts(input);
7376
+ if (artifactResult.blocked.length) {
7377
+ return blockedHandoff({
7378
+ ...common,
7379
+ artifacts: artifactResult,
7380
+ message: `Implementation handoff is blocked because ${artifactResult.blocked.length} approved artifact${artifactResult.blocked.length === 1 ? "" : "s"} could not be materialized safely.`
7381
+ });
7382
+ }
6378
7383
  const unmergedFiles = await gitOutput2(run, input.worktreePath, ["diff", "--name-only", "--diff-filter=U"]);
6379
7384
  if (unmergedFiles.trim()) {
6380
- return blockedHandoff({ ...common, message: "Implementation handoff is blocked because the worktree has unresolved merge conflicts." });
7385
+ return blockedHandoff({ ...common, artifacts: artifactResult, message: "Implementation handoff is blocked because the worktree has unresolved merge conflicts." });
6381
7386
  }
6382
7387
  const status = await gitOutput2(run, input.worktreePath, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
6383
7388
  if (!status) {
@@ -6385,6 +7390,7 @@ async function completeImplementationHandoff(input) {
6385
7390
  ...common,
6386
7391
  status: "noChanges",
6387
7392
  cleanupStatus: "notApplicable",
7393
+ artifacts: artifactResult,
6388
7394
  message: "Local execution completed with no repository changes to hand off."
6389
7395
  };
6390
7396
  }
@@ -6396,14 +7402,16 @@ async function completeImplementationHandoff(input) {
6396
7402
  ...common,
6397
7403
  provider,
6398
7404
  remoteName,
7405
+ artifacts: artifactResult,
6399
7406
  message: "Automated pull request handoff currently requires a GitHub remote. Commit and push manually, or link a GitHub repository."
6400
7407
  });
6401
7408
  }
6402
7409
  await gitOutput2(run, input.worktreePath, ["add", "-A"]);
6403
7410
  await gitOutput2(run, input.worktreePath, ["commit", "-m", commitSubject(input.workItem), "-m", commitBody(input)]);
7411
+ await rebaseBranchFromRemoteBase(run, input.worktreePath, { baseBranch, remoteName });
6404
7412
  const commitSha = await gitOutput2(run, input.worktreePath, ["rev-parse", "HEAD"]);
6405
7413
  await gitOutput2(run, input.worktreePath, ["push", "--set-upstream", remoteName, headBranch]);
6406
- const pullRequest = await ensureGithubPullRequest(run, input.worktreePath, { baseBranch, headBranch, workItem: input.workItem, ...input.verificationSummary ? { verificationSummary: input.verificationSummary } : {} });
7414
+ const pullRequest = await ensureGithubPullRequest(run, input.worktreePath, { artifacts: artifactResult, baseBranch, headBranch, workItem: input.workItem, ...input.verificationSummary ? { verificationSummary: input.verificationSummary } : {} });
6407
7415
  const cleanup = await cleanupWorktree(run, input);
6408
7416
  return {
6409
7417
  provider: "github",
@@ -6416,12 +7424,70 @@ async function completeImplementationHandoff(input) {
6416
7424
  prUrl: pullRequest.url,
6417
7425
  cleanupStatus: cleanup.status,
6418
7426
  ...cleanup.message ? { cleanupMessage: cleanup.message } : {},
7427
+ artifacts: artifactResult,
6419
7428
  message: cleanup.status === "completed" ? "GitHub pull request is ready for review and the local worktree was cleaned up." : "GitHub pull request is ready for review; local worktree cleanup needs attention."
6420
7429
  };
6421
7430
  } catch (error) {
6422
7431
  return blockedHandoff({ ...common, message: "Implementation handoff is blocked and the local worktree was preserved for recovery.", error: safeErrorMessage(error) });
6423
7432
  }
6424
7433
  }
7434
+ async function materializeApprovedArtifacts(input) {
7435
+ const { selected: artifacts, skipped } = selectApprovedWorkArtifacts(input.workItem, input.approvedArtifacts ?? []);
7436
+ if (!artifacts.length) {
7437
+ return { included: [], skipped, blocked: [] };
7438
+ }
7439
+ let materialized;
7440
+ try {
7441
+ materialized = await materializeBrainDocuments(input.worktreePath, artifacts);
7442
+ } catch (error) {
7443
+ return { included: [], skipped, blocked: artifacts.map((artifact) => artifactStatus(artifact, "blocked", safeErrorMessage(error))) };
7444
+ }
7445
+ return {
7446
+ included: materialized.written.map((repoPath) => artifactStatus(findArtifactByPath(artifacts, repoPath), "included", "Approved artifact materialized into the implementation worktree.")),
7447
+ skipped: [...skipped, ...materialized.skipped.map((repoPath) => artifactStatus(findArtifactByPath(artifacts, repoPath), "skipped", "Approved artifact was already current in the implementation worktree."))],
7448
+ blocked: materialized.conflicts.map((conflict) => artifactStatus(findArtifactByPath(artifacts, conflictRepoPath(conflict)), "blocked", conflict))
7449
+ };
7450
+ }
7451
+ function selectApprovedWorkArtifacts(workItem, documents) {
7452
+ const draftId = workItem.generatedDraftId;
7453
+ const explicitDocumentIds = new Set([workItem.reviewDocumentId, workItem.impactDocumentId].filter((value) => Boolean(value)));
7454
+ const scopeIds = new Set([workItem.workItemId, workItem.controllingAdrId, workItem.implementationScopeId].filter((value) => Boolean(value)));
7455
+ const skipped = [];
7456
+ const selected = documents.filter((document) => {
7457
+ const inScope = Boolean(draftId && document.frontmatter.generatedDraftId === draftId || explicitDocumentIds.has(document.documentId) || explicitDocumentIds.has(document.id) || [document.frontmatter.workItemId, document.frontmatter.generationWorkItemId, document.frontmatter.revisionWorkItemId, document.frontmatter.controllingAdrId, document.frontmatter.adrId, document.frontmatter.implementationScopeId].some((value) => typeof value === "string" && scopeIds.has(value)));
7458
+ if (!inScope) return false;
7459
+ if (document.status !== "approved" && document.syncState !== "approved" && document.syncState !== "synced") {
7460
+ skipped.push(artifactStatus(document, "skipped", "Artifact is not approved for repository inclusion."));
7461
+ return false;
7462
+ }
7463
+ if (!["markdown", "html", void 0].includes(document.contentFormat)) {
7464
+ skipped.push(artifactStatus(document, "skipped", "Artifact format is not supported for implementation PR inclusion."));
7465
+ return false;
7466
+ }
7467
+ return true;
7468
+ });
7469
+ const byDocumentId = /* @__PURE__ */ new Map();
7470
+ for (const document of selected) {
7471
+ byDocumentId.set(document.documentId, document);
7472
+ }
7473
+ return { selected: [...byDocumentId.values()], skipped };
7474
+ }
7475
+ function findArtifactByPath(artifacts, repoPath) {
7476
+ return artifacts.find((artifact) => artifact.repoPath === repoPath) ?? artifacts[0];
7477
+ }
7478
+ function artifactStatus(artifact, status, message) {
7479
+ return {
7480
+ documentId: artifact.documentId,
7481
+ title: truncate(artifact.title, 200),
7482
+ repoPath: artifact.repoPath,
7483
+ contentFormat: artifact.contentFormat ?? "markdown",
7484
+ status,
7485
+ message: truncate(message, 600)
7486
+ };
7487
+ }
7488
+ function conflictRepoPath(conflict) {
7489
+ return conflict.split(":")[0]?.trim() ?? conflict;
7490
+ }
6425
7491
  async function ensureGithubPullRequest(run, cwd, input) {
6426
7492
  const existing = await run("gh", ["pr", "list", "--head", input.headBranch, "--base", input.baseBranch, "--state", "open", "--json", "number,url", "--limit", "1"], { cwd });
6427
7493
  const parsed = parsePullRequestList(existing.stdout);
@@ -6458,6 +7524,10 @@ async function resolveRemoteName(run, cwd) {
6458
7524
  }
6459
7525
  return remotes.includes("origin") ? "origin" : remotes[0];
6460
7526
  }
7527
+ async function rebaseBranchFromRemoteBase(run, cwd, input) {
7528
+ await gitOutput2(run, cwd, ["fetch", input.remoteName, input.baseBranch]);
7529
+ await gitOutput2(run, cwd, ["rebase", "FETCH_HEAD"]);
7530
+ }
6461
7531
  async function gitOutput2(run, cwd, args) {
6462
7532
  const result = await run("git", args, { cwd });
6463
7533
  return result.stdout.trim();
@@ -6513,9 +7583,23 @@ function pullRequestBody(input) {
6513
7583
  `Base branch: ${input.baseBranch}`,
6514
7584
  `Head branch: ${input.headBranch}`,
6515
7585
  "",
7586
+ ...artifactSummaryLines(input.artifacts),
7587
+ "",
6516
7588
  input.verificationSummary ?? "Verification summary was not reported by the local runner."
6517
7589
  ].join("\n");
6518
7590
  }
7591
+ function artifactSummaryLines(artifacts) {
7592
+ if (!artifacts || !artifacts.included.length && !artifacts.skipped.length && !artifacts.blocked.length) {
7593
+ return ["Approved artifacts: none materialized for this handoff."];
7594
+ }
7595
+ const lines = [
7596
+ `Approved artifacts: ${artifacts.included.length} included, ${artifacts.skipped.length} already current, ${artifacts.blocked.length} blocked.`
7597
+ ];
7598
+ for (const artifact of artifacts.included.slice(0, 12)) {
7599
+ lines.push(`- ${artifact.repoPath}${artifact.title ? ` (${artifact.title})` : ""}`);
7600
+ }
7601
+ return lines;
7602
+ }
6519
7603
  function truncate(value, maxLength) {
6520
7604
  return value.length <= maxLength ? value : value.slice(0, maxLength - 1).trimEnd();
6521
7605
  }
@@ -6556,7 +7640,8 @@ var DEFAULT_MAX_PREFLIGHT_ATTEMPTS = 3;
6556
7640
  var DEFAULT_TOOL_TIMEOUT_SECONDS = 30 * 60;
6557
7641
  var RUNNER_WORK_LEASE_SECONDS = 300;
6558
7642
  var RUNNER_WORK_LEASE_RENEWAL_MS = 12e4;
6559
- var runnerSupportedWorkKinds = ["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification"];
7643
+ var MAX_CONCURRENT_RUNNER_WORK = 4;
7644
+ var runnerSupportedWorkKinds = ["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification", "testQualityScan", "implementationTestGate"];
6560
7645
  program.name("amistio").description("Amistio project brain CLI").version(CLI_VERSION);
6561
7646
  program.command("init").description("Create Amistio control-plane folders for a new project").option("--root <path>", "Repository root", defaultRoot).action(async (options) => {
6562
7647
  const created = await initControlPlane(options.root);
@@ -6839,7 +7924,7 @@ work.command("list").description("List queued work without claiming it").option(
6839
7924
  return;
6840
7925
  }
6841
7926
  for (const item of workItems) {
6842
- console.log(`${item.workItemId} [${item.status}] ${item.title}`);
7927
+ console.log(`${item.workItemId} [${item.status}] ${item.title}${formatAutopilotListSuffix(item)}`);
6843
7928
  }
6844
7929
  });
6845
7930
  work.command("prompt").description("Print or write an approved work prompt without claiming a runner lease").argument("[workItemId]", "Work item ID. Defaults to the newest approved work item.").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).option("--out <path>", "Write the prompt to a file instead of stdout").action(async (workItemId, options) => {
@@ -6905,7 +7990,7 @@ program.command("orchestrate").description("Update the Amistio control plane thr
6905
7990
  process.exitCode = result.exitCode;
6906
7991
  }
6907
7992
  });
6908
- program.command("run").description("Claim and run approved Amistio work locally").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--runner-id <runnerId>", "Stable runner ID").option("--root <path>", "Repository root", defaultRoot).option("--tool <name>", "Local tool to use: auto, none, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--tool-command <command>", "Custom local command. Use {promptFile} and {root} placeholders when supported").option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--dry-run", "Claim work and print the generated execution prompt without running a tool").option("--watch", "Keep polling for approved work until stopped").option("--background", "Start a detached background runner that watches for approved work").option("--interval-seconds <seconds>", "Polling interval for --watch", parsePositiveInteger, 10).option("--max-iterations <count>", "Stop watch mode after this many polling attempts", parsePositiveInteger).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors while watching").action(async (options, command) => {
7993
+ program.command("run").description("Claim and run approved Amistio work locally").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--runner-id <runnerId>", "Stable runner ID").option("--root <path>", "Repository root", defaultRoot).option("--tool <name>", "Local tool to use: auto, none, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--tool-command <command>", "Custom local command. Use {promptFile} and {root} placeholders when supported").option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--dry-run", "Claim work and print the generated execution prompt without running a tool").option("--watch", "Keep polling for approved work until stopped").option("--background", "Start a detached background runner that watches for approved work").option("--interval-seconds <seconds>", "Polling interval for --watch", parsePositiveInteger, 10).option("--max-iterations <count>", "Stop watch mode after this many polling attempts", parsePositiveInteger).option("--max-concurrent-work <count>", "Maximum approved work items to run in parallel in --watch mode", parsePositiveInteger, 1).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors while watching").action(async (options, command) => {
6909
7994
  const context = await loadPairedApiContext(options.root, options.apiUrl);
6910
7995
  if (!context) {
6911
7996
  console.log("Repository is not paired. Run `amistio pair` first.");
@@ -6923,6 +8008,11 @@ program.command("run").description("Claim and run approved Amistio work locally"
6923
8008
  machineId: runnerMachineId()
6924
8009
  });
6925
8010
  const resolvedOptions = { ...options, runnerId };
8011
+ if (resolvedOptions.maxConcurrentWork > MAX_CONCURRENT_RUNNER_WORK) {
8012
+ console.log(`--max-concurrent-work is capped at ${MAX_CONCURRENT_RUNNER_WORK}.`);
8013
+ process.exitCode = 1;
8014
+ return;
8015
+ }
6926
8016
  if (options.background) {
6927
8017
  if (options.dryRun) {
6928
8018
  console.log("Background runners cannot be started in dry-run mode.");
@@ -7077,7 +8167,7 @@ runner.command("stop").description("Stop a background runner for the paired repo
7077
8167
  console.log(stopResult === "stopped" ? `Stopped background runner ${record.runnerId}.` : `Marked background runner ${record.runnerId} stopped; process was not running.`);
7078
8168
  });
7079
8169
  var runnerService = runner.command("service").description("Manage a user-level startup service for the paired runner");
7080
- runnerService.command("install").description("Install a user-level startup service for this paired repository runner").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).option("--runner-id <runnerId>", "Stable runner ID").option("--tool <name>", "Local tool to use: auto, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--interval-seconds <seconds>", "Polling interval for the service runner", parsePositiveInteger, 10).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors").option("--dry-run", "Print the startup service descriptor without installing it").action(async (options) => {
8170
+ runnerService.command("install").description("Install a user-level startup service for this paired repository runner").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).option("--runner-id <runnerId>", "Stable runner ID").option("--tool <name>", "Local tool to use: auto, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--interval-seconds <seconds>", "Polling interval for the service runner", parsePositiveInteger, 10).option("--max-concurrent-work <count>", "Maximum approved work items to run in parallel in --watch mode", parsePositiveInteger, 1).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors").option("--dry-run", "Print the startup service descriptor without installing it").action(async (options) => {
7081
8171
  const context = await loadPairedApiContext(options.root, options.apiUrl);
7082
8172
  if (!context) {
7083
8173
  console.log("Repository is not paired. Run `amistio pair` first.");
@@ -7100,6 +8190,11 @@ runnerService.command("install").description("Install a user-level startup servi
7100
8190
  repositoryLinkId: context.metadata.repositoryLinkId,
7101
8191
  machineId: runnerMachineId()
7102
8192
  });
8193
+ if (options.maxConcurrentWork > MAX_CONCURRENT_RUNNER_WORK) {
8194
+ console.log(`--max-concurrent-work is capped at ${MAX_CONCURRENT_RUNNER_WORK}.`);
8195
+ process.exitCode = 1;
8196
+ return;
8197
+ }
7103
8198
  const args = buildBackgroundRunnerArgs({ ...options, runnerId, apiUrl: options.apiUrl, root: options.root });
7104
8199
  const serviceInput = {
7105
8200
  accountId: context.metadata.amistioAccountId,
@@ -7178,7 +8273,7 @@ async function runWatchIteration({ command, context, options, runnerId }) {
7178
8273
  });
7179
8274
  }
7180
8275
  if (!options.dryRun) {
7181
- const replayResult = await replayPendingBrainGenerationFinalizations({
8276
+ const replayResult = await replayPendingResultFinalizations({
7182
8277
  accountId: context.metadata.amistioAccountId,
7183
8278
  apiClient: context.client,
7184
8279
  projectId: context.metadata.amistioProjectId,
@@ -7189,7 +8284,8 @@ async function runWatchIteration({ command, context, options, runnerId }) {
7189
8284
  return replayResult;
7190
8285
  }
7191
8286
  }
7192
- return await runNextWorkItem({
8287
+ const activeClaimLaneIds = claimLaneIds(options.maxConcurrentWork);
8288
+ const runLane = (claimLaneId, laneIndex) => runNextWorkItem({
7193
8289
  apiClient: context.client,
7194
8290
  projectId: context.metadata.amistioProjectId,
7195
8291
  repositoryLinkId: context.metadata.repositoryLinkId,
@@ -7218,8 +8314,16 @@ async function runWatchIteration({ command, context, options, runnerId }) {
7218
8314
  suppressIdleOutput: Boolean(options.watch),
7219
8315
  maxPreflightAttempts: options.maxPreflightAttempts,
7220
8316
  toolTimeoutMs: options.toolTimeoutSeconds * 1e3,
7221
- verbose: Boolean(options.verbose)
8317
+ verbose: Boolean(options.verbose),
8318
+ claimLaneId,
8319
+ maxConcurrentWork: options.maxConcurrentWork,
8320
+ activeClaimLaneIds,
8321
+ skipRunnerCommands: laneIndex > 0
7222
8322
  });
8323
+ if (options.watch && !options.dryRun && options.maxConcurrentWork > 1) {
8324
+ return aggregateRunnerLaneResults(await Promise.all(activeClaimLaneIds.map((claimLaneId, laneIndex) => runLane(claimLaneId, laneIndex))));
8325
+ }
8326
+ return await runLane("default", 0);
7223
8327
  } catch (error) {
7224
8328
  if (!options.watch) {
7225
8329
  throw error;
@@ -7247,6 +8351,23 @@ ${detail}`);
7247
8351
  return { status: "failed", exitCode: 1, message };
7248
8352
  }
7249
8353
  }
8354
+ function claimLaneIds(maxConcurrentWork) {
8355
+ return Array.from({ length: maxConcurrentWork }, (_, index) => index === 0 ? "default" : `lane_${index + 1}`);
8356
+ }
8357
+ function supportsConcurrentLocalToolExecution(toolConfig) {
8358
+ return toolConfig.tool === "none" || toolConfig.effectiveInvocationChannel === "command" || toolConfig.effectiveTool === "custom";
8359
+ }
8360
+ function aggregateRunnerLaneResults(results) {
8361
+ const stopResult = results.find((result) => result.stopRunner);
8362
+ if (stopResult) return stopResult;
8363
+ const failedResult = results.find((result) => result.status === "failed");
8364
+ if (failedResult) return failedResult;
8365
+ const blockedResult = results.find((result) => result.status === "blocked");
8366
+ if (blockedResult) return blockedResult;
8367
+ const completedResult = results.find((result) => result.status === "completed" || result.status === "preview");
8368
+ if (completedResult) return completedResult;
8369
+ return results.find((result) => result.status === "idle") ?? { status: "idle", exitCode: 0 };
8370
+ }
7250
8371
  async function runAutoSyncCycle({ context, maxFileKb, quiet, quietDisabled, root, runnerId }) {
7251
8372
  const projectId = context.metadata.amistioProjectId;
7252
8373
  const repositoryLinkId = context.metadata.repositoryLinkId;
@@ -7326,7 +8447,11 @@ async function runNextWorkItem({
7326
8447
  commandContext,
7327
8448
  suppressIdleOutput,
7328
8449
  toolTimeoutMs,
7329
- verbose
8450
+ verbose,
8451
+ claimLaneId,
8452
+ maxConcurrentWork,
8453
+ activeClaimLaneIds,
8454
+ skipRunnerCommands
7330
8455
  }) {
7331
8456
  const toolConfig = await resolveRunnerToolConfig({
7332
8457
  apiClient,
@@ -7340,19 +8465,27 @@ async function runNextWorkItem({
7340
8465
  ...explicitTool ? { explicitTool } : {},
7341
8466
  ...toolCommand ? { toolCommand } : {}
7342
8467
  });
7343
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, toolConfig.ready ? "online" : "blocked", runnerHeartbeatMetadata(toolConfig));
7344
- const commandResult = await runPendingRunnerCommand(apiClient, commandContext, runnerHeartbeatMetadata(toolConfig));
7345
- if (commandResult.handled) {
7346
- if (commandResult.message) {
7347
- console.log(commandResult.message);
8468
+ const effectiveMaxConcurrentWork = supportsConcurrentLocalToolExecution(toolConfig) ? maxConcurrentWork : 1;
8469
+ const effectiveActiveClaimLaneIds = claimLaneIds(effectiveMaxConcurrentWork);
8470
+ const heartbeatConcurrency = { maxConcurrentWork: effectiveMaxConcurrentWork, activeClaimLaneIds: effectiveActiveClaimLaneIds };
8471
+ if (claimLaneId !== "default" && effectiveMaxConcurrentWork === 1) {
8472
+ return { status: "idle", exitCode: 0 };
8473
+ }
8474
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, toolConfig.ready ? "online" : "blocked", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
8475
+ if (!skipRunnerCommands) {
8476
+ const commandResult = await runPendingRunnerCommand(apiClient, commandContext, runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
8477
+ if (commandResult.handled) {
8478
+ if (commandResult.message) {
8479
+ console.log(commandResult.message);
8480
+ }
8481
+ return { status: commandResult.succeeded ? "completed" : "failed", exitCode: commandResult.succeeded ? 0 : 1, ...commandResult.stopRunner ? { stopRunner: true } : {} };
7348
8482
  }
7349
- return { status: commandResult.succeeded ? "completed" : "failed", exitCode: commandResult.succeeded ? 0 : 1, ...commandResult.stopRunner ? { stopRunner: true } : {} };
7350
8483
  }
7351
8484
  if (!toolConfig.ready) {
7352
8485
  console.log(toolConfig.message);
7353
8486
  return { status: "blocked", exitCode: 1 };
7354
8487
  }
7355
- const result = await apiClient.claimWork(projectId, runnerId, repositoryLinkId, RUNNER_WORK_LEASE_SECONDS, runnerIsolationCapabilityMetadata());
8488
+ const result = await apiClient.claimWork(projectId, runnerId, repositoryLinkId, RUNNER_WORK_LEASE_SECONDS, runnerIsolationCapabilityMetadata({ claimLaneId, maxConcurrentWork: effectiveMaxConcurrentWork }));
7356
8489
  if (!result.workItem) {
7357
8490
  const nextAction = await loadProjectNextAction(apiClient, projectId, repositoryLinkId, root);
7358
8491
  const message = formatProjectNextAction(nextAction);
@@ -7370,17 +8503,17 @@ async function runNextWorkItem({
7370
8503
  });
7371
8504
  if (dryRun || toolConfig.tool === "none") {
7372
8505
  console.log(prompt);
7373
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
8506
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
7374
8507
  return { status: "preview", exitCode: 0 };
7375
8508
  }
7376
- const worktreeIsolation = await prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem: result.workItem });
8509
+ const worktreeIsolation = await prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem: result.workItem });
7377
8510
  if (worktreeIsolation.status !== "ready") {
7378
8511
  return { status: worktreeIsolation.status === "failed" ? "failed" : "blocked", exitCode: 1, message: worktreeIsolation.message };
7379
8512
  }
7380
8513
  const executionRoot = worktreeIsolation.isolation?.worktreePath ?? root;
7381
8514
  const isolationTelemetry = workItemIsolationTelemetry(result.workItem, worktreeIsolation.isolation);
7382
8515
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "running", {
7383
- ...runnerHeartbeatMetadata(toolConfig),
8516
+ ...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
7384
8517
  currentWorkItemId: result.workItem.workItemId,
7385
8518
  ...isolationTelemetry.implementationScopeId ? { currentImplementationScopeId: isolationTelemetry.implementationScopeId } : {},
7386
8519
  ...isolationTelemetry.executionWorktreeKey ? { currentWorktreeKey: isolationTelemetry.executionWorktreeKey } : {},
@@ -7403,6 +8536,10 @@ async function runNextWorkItem({
7403
8536
  isolationTelemetry
7404
8537
  });
7405
8538
  console.log(`Claimed ${result.workItem.workItemId}. Running ${preview.toolName}: ${preview.displayCommand}`);
8539
+ const autopilotClaimLine = formatAutopilotClaimLine(result.workItem);
8540
+ if (autopilotClaimLine) {
8541
+ console.log(autopilotClaimLine);
8542
+ }
7406
8543
  await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
7407
8544
  status: "running",
7408
8545
  summary: `Local runner started ${preview.toolName} execution.`,
@@ -7413,7 +8550,7 @@ async function runNextWorkItem({
7413
8550
  const providerSessionStore = new LocalToolSessionStore();
7414
8551
  const providerSessionId = sessionContext.toolSession ? await providerSessionStore.getProviderSessionId(sessionContext.toolSession.toolSessionId, preview.toolName) : void 0;
7415
8552
  let toolResult;
7416
- const stopLeaseRenewal = startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem: result.workItem, telemetry: isolationTelemetry });
8553
+ const stopLeaseRenewal = startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem: result.workItem, telemetry: isolationTelemetry, heartbeatConcurrency });
7417
8554
  try {
7418
8555
  toolResult = await runLocalTool({
7419
8556
  rootDir: executionRoot,
@@ -7439,7 +8576,7 @@ async function runNextWorkItem({
7439
8576
  const durationMs2 = Date.now() - startedAt;
7440
8577
  const message = `${preview.toolName} failed before returning a result.`;
7441
8578
  const settlements = await Promise.allSettled([
7442
- apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig)),
8579
+ apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency)),
7443
8580
  markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage3(error)),
7444
8581
  apiClient.updateWorkStatus(projectId, result.workItem.workItemId, "failed", `run_failed_${result.workItem.workItemId}_${result.workItem.attempt}_${runnerId}`, runnerId, {
7445
8582
  ...isolationTelemetry,
@@ -7622,6 +8759,42 @@ async function runNextWorkItem({
7622
8759
  return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
7623
8760
  }
7624
8761
  }
8762
+ if (result.workItem.workKind === "testQualityScan") {
8763
+ try {
8764
+ return await finalizeTestQualityScanWork({
8765
+ apiClient,
8766
+ durationMs: Date.now() - startedAt,
8767
+ projectId,
8768
+ repositoryLinkId,
8769
+ runnerId,
8770
+ sessionContext,
8771
+ toolConfig,
8772
+ toolName: preview.toolName,
8773
+ toolResult,
8774
+ workItem: result.workItem
8775
+ });
8776
+ } catch (error) {
8777
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
8778
+ }
8779
+ }
8780
+ if (result.workItem.workKind === "implementationTestGate") {
8781
+ try {
8782
+ return await finalizeImplementationTestGateWork({
8783
+ apiClient,
8784
+ durationMs: Date.now() - startedAt,
8785
+ projectId,
8786
+ repositoryLinkId,
8787
+ runnerId,
8788
+ sessionContext,
8789
+ toolConfig,
8790
+ toolName: preview.toolName,
8791
+ toolResult,
8792
+ workItem: result.workItem
8793
+ });
8794
+ } catch (error) {
8795
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
8796
+ }
8797
+ }
7625
8798
  let finalStatus = toolResult.exitCode === 0 ? "completed" : "failed";
7626
8799
  const durationMs = Date.now() - startedAt;
7627
8800
  const failureExcerpt = toolResult.exitCode === 0 ? void 0 : truncateLogExcerpt(toolResult.stderr || toolResult.stdout);
@@ -7636,14 +8809,27 @@ async function runNextWorkItem({
7636
8809
  metadata: { executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
7637
8810
  });
7638
8811
  const repositoryLink = await loadWorkItemRepositoryLink(apiClient, projectId, result.workItem.repositoryLinkId ?? repositoryLinkId);
7639
- implementationHandoff = await completeImplementationHandoff({
7640
- primaryRepoRoot: root,
7641
- ...repositoryLink ? { repositoryLink } : {},
7642
- verificationSummary: "Local execution reported completion.",
7643
- workItem: result.workItem,
7644
- ...worktreeIsolation.isolation ? { worktreeIsolation: worktreeIsolation.isolation } : {},
7645
- worktreePath: executionRoot
8812
+ const approvedArtifacts = await apiClient.listBrainDocuments(projectId).then((response) => response.documents).catch((error) => {
8813
+ implementationHandoff = {
8814
+ status: "blocked",
8815
+ cleanupStatus: "pending",
8816
+ artifacts: { included: [], skipped: [], blocked: [] },
8817
+ message: "Implementation handoff is blocked because approved artifact metadata could not be loaded.",
8818
+ error: truncateLogExcerpt(errorDetail(error))
8819
+ };
8820
+ return void 0;
7646
8821
  });
8822
+ if (!implementationHandoff) {
8823
+ implementationHandoff = await completeImplementationHandoff({
8824
+ ...approvedArtifacts ? { approvedArtifacts } : {},
8825
+ primaryRepoRoot: root,
8826
+ ...repositoryLink ? { repositoryLink } : {},
8827
+ verificationSummary: "Local execution reported completion.",
8828
+ workItem: result.workItem,
8829
+ ...worktreeIsolation.isolation ? { worktreeIsolation: worktreeIsolation.isolation } : {},
8830
+ worktreePath: executionRoot
8831
+ });
8832
+ }
7647
8833
  finalStatus = implementationHandoff.status === "prReady" || implementationHandoff.status === "noChanges" ? "completed" : "blocked";
7648
8834
  finalMessage = implementationHandoff.message ?? "Implementation handoff finished.";
7649
8835
  finalError = implementationHandoff.error;
@@ -7661,31 +8847,48 @@ async function runNextWorkItem({
7661
8847
  ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
7662
8848
  ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
7663
8849
  });
7664
- const statusResult = await apiClient.updateWorkStatus(
7665
- projectId,
7666
- result.workItem.workItemId,
7667
- finalStatus,
7668
- `run_${result.workItem.workItemId}_${randomUUID()}`,
7669
- runnerId,
7670
- {
7671
- tool: preview.toolName,
7672
- ...toolResult.model ? { model: toolResult.model } : {},
7673
- durationMs,
7674
- message: finalMessage,
7675
- ...isolationTelemetry,
7676
- ...finalStatus === "blocked" ? { blockerReason: finalMessage } : {},
7677
- sessionPolicy: sessionContext.policy,
7678
- sessionDecision: sessionContext.decision,
7679
- sessionDecisionReason: sessionContext.reason,
7680
- ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
7681
- ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {},
7682
- ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
7683
- ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
7684
- ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {},
7685
- ...implementationHandoff ? { implementationHandoff } : {},
7686
- ...finalError ? { error: finalError } : {}
8850
+ let statusResult;
8851
+ try {
8852
+ statusResult = await apiClient.updateWorkStatus(
8853
+ projectId,
8854
+ result.workItem.workItemId,
8855
+ finalStatus,
8856
+ `run_${result.workItem.workItemId}_${randomUUID()}`,
8857
+ runnerId,
8858
+ {
8859
+ tool: preview.toolName,
8860
+ ...toolResult.model ? { model: toolResult.model } : {},
8861
+ durationMs,
8862
+ message: finalMessage,
8863
+ ...isolationTelemetry,
8864
+ ...finalStatus === "blocked" ? { blockerReason: finalMessage } : {},
8865
+ sessionPolicy: sessionContext.policy,
8866
+ sessionDecision: sessionContext.decision,
8867
+ sessionDecisionReason: sessionContext.reason,
8868
+ ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
8869
+ ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {},
8870
+ ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
8871
+ ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
8872
+ ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {},
8873
+ ...implementationHandoff ? { implementationHandoff } : {},
8874
+ ...finalError ? { error: finalError } : {}
8875
+ }
8876
+ );
8877
+ } catch (error) {
8878
+ if (error instanceof AmistioApiError && error.status === 409 && error.detail.includes("implementation_test_gate_required")) {
8879
+ const gateMessage = "Implementation test gate was queued and must pass before completion or PR handoff is finalized.";
8880
+ await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
8881
+ status: "queued",
8882
+ summary: gateMessage,
8883
+ idempotencyKey: `runner_milestone_test_gate_required_${result.workItem.workItemId}_${result.workItem.attempt}`,
8884
+ metadata: { tool: preview.toolName, durationMs, executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
8885
+ });
8886
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
8887
+ console.log(gateMessage);
8888
+ return { status: "blocked", exitCode: 0 };
7687
8889
  }
7688
- );
8890
+ throw error;
8891
+ }
7689
8892
  await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
7690
8893
  status: finalStatus,
7691
8894
  summary: finalMessage,
@@ -7699,19 +8902,27 @@ async function runNextWorkItem({
7699
8902
  executionBranch: isolationTelemetry.executionBranch ?? "",
7700
8903
  ...implementationHandoff?.status ? { handoffStatus: implementationHandoff.status } : {},
7701
8904
  ...implementationHandoff?.prUrl ? { prUrl: implementationHandoff.prUrl } : {},
7702
- ...implementationHandoff?.cleanupStatus ? { cleanupStatus: implementationHandoff.cleanupStatus } : {}
8905
+ ...implementationHandoff?.cleanupStatus ? { cleanupStatus: implementationHandoff.cleanupStatus } : {},
8906
+ ...implementationHandoff?.artifacts ? artifactHandoffMetadata(implementationHandoff.artifacts) : {}
7703
8907
  }
7704
8908
  });
7705
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
8909
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
7706
8910
  const durationSeconds = Math.round(durationMs / 1e3);
7707
8911
  console.log(`Marked ${statusResult.workItem.workItemId} ${statusResult.workItem.status} after ${durationSeconds}s.`);
7708
8912
  return { status: finalStatus, exitCode: finalStatus === "completed" ? toolResult.exitCode : 1 };
7709
8913
  }
8914
+ function artifactHandoffMetadata(artifacts) {
8915
+ return {
8916
+ artifactIncludedCount: artifacts.included.length,
8917
+ artifactSkippedCount: artifacts.skipped.length,
8918
+ artifactBlockedCount: artifacts.blocked.length
8919
+ };
8920
+ }
7710
8921
  async function loadWorkItemRepositoryLink(apiClient, projectId, repositoryLinkId) {
7711
8922
  const { repositoryLinks } = await apiClient.listRepositoryLinks(projectId);
7712
8923
  return repositoryLinks.find((link) => link.repositoryLinkId === repositoryLinkId && link.status !== "revoked");
7713
8924
  }
7714
- async function prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem }) {
8925
+ async function prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem }) {
7715
8926
  if (!needsGitWorktreeIsolation(workItem)) {
7716
8927
  return { status: "ready" };
7717
8928
  }
@@ -7726,9 +8937,9 @@ async function prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts,
7726
8937
  const isolation = await prepareGitWorktreeIsolation(root, workItem);
7727
8938
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
7728
8939
  status: "running",
7729
- summary: `Prepared Git worktree ${isolation.worktreeKey} on ${isolation.branch}.`,
8940
+ summary: isolation.preparedLocalEnvironmentFileCount ? `Prepared Git worktree ${isolation.worktreeKey} on ${isolation.branch} and local environment files.` : `Prepared Git worktree ${isolation.worktreeKey} on ${isolation.branch}.`,
7730
8941
  idempotencyKey: `runner_milestone_worktree_${workItem.workItemId}_${workItem.attempt}`,
7731
- metadata: { executionWorktreeKey: isolation.worktreeKey, executionBranch: isolation.branch, implementationScopeId: isolation.implementationScopeId }
8942
+ metadata: { executionWorktreeKey: isolation.worktreeKey, executionBranch: isolation.branch, implementationScopeId: isolation.implementationScopeId, ...isolation.preparedLocalEnvironmentFileCount ? { preparedLocalEnvironmentFileCount: isolation.preparedLocalEnvironmentFileCount } : {} }
7732
8943
  });
7733
8944
  return { status: "ready", isolation };
7734
8945
  } catch (error) {
@@ -7749,14 +8960,14 @@ async function prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts,
7749
8960
  metadata: { executionWorktreeKey: telemetry.executionWorktreeKey ?? "", executionBranch: telemetry.executionBranch ?? "", implementationScopeId: telemetry.implementationScopeId ?? "", attempt: workItem.attempt, maxAttempts: maxPreflightAttempts, error: message }
7750
8961
  });
7751
8962
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", {
7752
- ...runnerHeartbeatMetadata(toolConfig),
8963
+ ...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
7753
8964
  preferenceMessage: statusMessage
7754
8965
  });
7755
8966
  console.error(statusMessage);
7756
8967
  return { status: finalAttempt ? "failed" : "retrying", message: statusMessage };
7757
8968
  }
7758
8969
  }
7759
- function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem, telemetry }) {
8970
+ function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem, telemetry, heartbeatConcurrency }) {
7760
8971
  let stopped = false;
7761
8972
  const renew = async () => {
7762
8973
  if (stopped) return;
@@ -7767,7 +8978,7 @@ function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerI
7767
8978
  leaseExpiresAt
7768
8979
  });
7769
8980
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "running", {
7770
- ...runnerHeartbeatMetadata(toolConfig),
8981
+ ...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
7771
8982
  currentWorkItemId: workItem.workItemId,
7772
8983
  ...telemetry.implementationScopeId ? { currentImplementationScopeId: telemetry.implementationScopeId } : {},
7773
8984
  ...telemetry.executionWorktreeKey ? { currentWorktreeKey: telemetry.executionWorktreeKey } : {},
@@ -7783,6 +8994,7 @@ function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerI
7783
8994
  workItemId: workItem.workItemId,
7784
8995
  workTitle: workItem.title,
7785
8996
  ...workItem.workKind ? { workKind: workItem.workKind } : {},
8997
+ ...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
7786
8998
  message: "Runner could not renew the active work lease.",
7787
8999
  error: detail,
7788
9000
  machineId: runnerMachineId()
@@ -7822,6 +9034,7 @@ async function recordFinalizationFailure({ apiClient, durationMs, error, isolati
7822
9034
  workItemId: workItem.workItemId,
7823
9035
  workTitle: workItem.title,
7824
9036
  ...workItem.workKind ? { workKind: workItem.workKind } : {},
9037
+ ...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
7825
9038
  tool: toolName,
7826
9039
  durationMs,
7827
9040
  message,
@@ -7846,6 +9059,7 @@ function workItemIsolationTelemetry(workItem, isolation) {
7846
9059
  const repositoryLockId = isolation?.repositoryLockId ?? workItem.repositoryLockId;
7847
9060
  return {
7848
9061
  ...needsGitWorktreeIsolation(workItem) ? { isolationMode: "gitWorktree" } : workItem.isolationMode ? { isolationMode: workItem.isolationMode } : {},
9062
+ ...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
7849
9063
  ...workItem.claimLeaseId ? { claimLeaseId: workItem.claimLeaseId } : {},
7850
9064
  ...workItem.controllingAdrId ? { controllingAdrId: workItem.controllingAdrId } : {},
7851
9065
  ...implementationScopeId ? { implementationScopeId } : {},
@@ -7857,19 +9071,62 @@ function workItemIsolationTelemetry(workItem, isolation) {
7857
9071
  };
7858
9072
  }
7859
9073
  async function recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, input) {
9074
+ const autopilot = autopilotWorkMetadata2(workItem);
9075
+ const metadata = { ...autopilotRunnerMetadata(workItem), ...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {}, ...input.metadata ?? {} };
7860
9076
  await apiClient.recordActivityEvent(projectId, {
7861
9077
  eventType: "runnerMilestone",
7862
9078
  runnerId,
7863
9079
  repositoryLinkId,
9080
+ ...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
7864
9081
  relatedWorkItemId: workItem.workItemId,
7865
9082
  ...workItem.reviewDocumentId ? { relatedDocumentId: workItem.reviewDocumentId } : workItem.impactDocumentId ? { relatedDocumentId: workItem.impactDocumentId } : {},
7866
9083
  ...workItem.issueId ? { relatedIssueId: workItem.issueId } : {},
9084
+ ...autopilot.autopilotAuthorizationId ? { relatedAutopilotAuthorizationId: autopilot.autopilotAuthorizationId } : {},
9085
+ ...autopilot.autopilotCandidateId ? { relatedAutopilotCandidateId: autopilot.autopilotCandidateId } : {},
7867
9086
  ...workItem.generatedDraftId ? { generatedDraftId: workItem.generatedDraftId } : {},
7868
- ...input
9087
+ ...input,
9088
+ ...Object.keys(metadata).length ? { metadata } : {}
7869
9089
  }).catch(() => void 0);
7870
9090
  }
7871
- function logRejectedSettlements(action, settlements) {
7872
- for (const settlement of settlements) {
9091
+ function formatAutopilotListSuffix(workItem) {
9092
+ const autopilot = autopilotWorkMetadata2(workItem);
9093
+ if (!autopilot.autopilotAuthorizationId) {
9094
+ return "";
9095
+ }
9096
+ const details = [
9097
+ autopilot.autopilotClassificationOutcome ?? "authorized",
9098
+ workItem.workKind ?? "implementation",
9099
+ autopilot.autopilotCandidateType,
9100
+ autopilot.autopilotPolicyVersion ? `policy ${autopilot.autopilotPolicyVersion}` : void 0,
9101
+ `auth ${autopilot.autopilotAuthorizationId}`,
9102
+ autopilot.autopilotCandidateId ? `candidate ${autopilot.autopilotCandidateId}` : void 0
9103
+ ].filter(Boolean).join(", ");
9104
+ return ` (autopilot: ${details})`;
9105
+ }
9106
+ function formatAutopilotClaimLine(workItem) {
9107
+ const autopilot = autopilotWorkMetadata2(workItem);
9108
+ if (!autopilot.autopilotAuthorizationId) {
9109
+ return void 0;
9110
+ }
9111
+ const candidate = autopilot.autopilotCandidateId ? `, candidate ${autopilot.autopilotCandidateId}` : "";
9112
+ const candidateType = autopilot.autopilotCandidateType ? `, ${autopilot.autopilotCandidateType}` : "";
9113
+ return `Autopilot authorization: ${autopilot.autopilotAuthorizationId} (${autopilot.autopilotClassificationOutcome ?? "authorized"}, ${workItem.workKind ?? "implementation"}${candidateType}, policy ${autopilot.autopilotPolicyVersion ?? "unknown"}${candidate}). Local runner safety rules still apply.`;
9114
+ }
9115
+ function autopilotRunnerMetadata(workItem) {
9116
+ const autopilot = autopilotWorkMetadata2(workItem);
9117
+ return {
9118
+ ...autopilot.autopilotAuthorizationId ? { autopilotAuthorizationId: autopilot.autopilotAuthorizationId } : {},
9119
+ ...autopilot.autopilotCandidateId ? { autopilotCandidateId: autopilot.autopilotCandidateId } : {},
9120
+ ...autopilot.autopilotCandidateType ? { autopilotCandidateType: autopilot.autopilotCandidateType } : {},
9121
+ ...autopilot.autopilotClassificationOutcome ? { autopilotOutcome: autopilot.autopilotClassificationOutcome } : {},
9122
+ ...autopilot.autopilotPolicyVersion ? { autopilotPolicyVersion: autopilot.autopilotPolicyVersion } : {}
9123
+ };
9124
+ }
9125
+ function autopilotWorkMetadata2(workItem) {
9126
+ return workItem;
9127
+ }
9128
+ function logRejectedSettlements(action, settlements) {
9129
+ for (const settlement of settlements) {
7873
9130
  if (settlement.status === "rejected") {
7874
9131
  console.error(`${action} failed: ${errorMessage3(settlement.reason)}`);
7875
9132
  }
@@ -7954,6 +9211,28 @@ async function replayPendingBrainGenerationFinalizations({ accountId, apiClient,
7954
9211
  console.log(message);
7955
9212
  return { status: "completed", exitCode: 0, message };
7956
9213
  }
9214
+ async function replayPendingResultFinalizations({ accountId, apiClient, projectId, repositoryLinkId, runnerId }) {
9215
+ const brainReplay = await replayPendingBrainGenerationFinalizations({ accountId, apiClient, projectId, repositoryLinkId, runnerId });
9216
+ if (brainReplay) {
9217
+ return brainReplay;
9218
+ }
9219
+ const pendingEntries = await listPendingDurableResultFinalizations({ accountId, projectId, repositoryLinkId, runnerId });
9220
+ if (!pendingEntries.length) {
9221
+ return void 0;
9222
+ }
9223
+ let completedCount = 0;
9224
+ for (const entry of pendingEntries) {
9225
+ const replay = await submitDurableResultFinalizationEntry(apiClient, entry, { recordReplayTelemetry: true });
9226
+ if (replay.status === "completed") {
9227
+ completedCount += 1;
9228
+ continue;
9229
+ }
9230
+ return { status: "failed", exitCode: 1, message: replay.message };
9231
+ }
9232
+ const message = `Replayed ${completedCount} pending runner result finalization${completedCount === 1 ? "" : "s"}.`;
9233
+ console.log(message);
9234
+ return { status: "completed", exitCode: 0, message };
9235
+ }
7957
9236
  async function submitBrainGenerationFinalizationEntry(apiClient, entry, options = {}) {
7958
9237
  let result;
7959
9238
  try {
@@ -7988,6 +9267,127 @@ async function submitBrainGenerationFinalizationEntry(apiClient, entry, options
7988
9267
  }
7989
9268
  return { status: "completed", workItem: result.workItem, documentCount: result.documents.length };
7990
9269
  }
9270
+ async function submitDurableResultFinalizationEntry(apiClient, entry, options = {}) {
9271
+ let response;
9272
+ try {
9273
+ response = await submitDurableResultMutation(apiClient, entry);
9274
+ } catch (error) {
9275
+ const detail = truncateLogExcerpt(errorMessage3(error));
9276
+ if (isRetryableApiError(error)) {
9277
+ const updated = await markDurableResultFinalizationRetry(entry, detail);
9278
+ const message2 = `Pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} could not be replayed yet (${updated.retryCount} attempt${updated.retryCount === 1 ? "" : "s"}): ${detail}`;
9279
+ console.error(message2);
9280
+ return { status: "failed", message: message2, retryable: true };
9281
+ }
9282
+ await markDurableResultFinalizationTerminal(entry, detail);
9283
+ const message = `Pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} reached a terminal API failure: ${detail}`;
9284
+ console.error(message);
9285
+ return { status: "failed", message, retryable: false };
9286
+ }
9287
+ const workItem = durableResultResponseWorkItem(response);
9288
+ await deleteDurableResultFinalizationEntry(entry).catch((error) => {
9289
+ console.error(`delete pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} failed: ${errorMessage3(error)}`);
9290
+ });
9291
+ if (options.recordReplayTelemetry) {
9292
+ await recordRunnerMilestone(apiClient, entry.projectId, workItem, entry.runnerId, entry.repositoryLinkId, {
9293
+ status: entry.result.status,
9294
+ summary: entry.result.status === "completed" ? `Replayed pending ${runnerResultFinalizationLabel(entry)} finalization.` : `Replayed pending ${runnerResultFinalizationLabel(entry)} failure finalization.`,
9295
+ idempotencyKey: `runner_milestone_result_replayed_${entry.workItemId}_${workItem.idempotencyKey}`,
9296
+ metadata: { replayedFinalization: true, resultKind: entry.resultKind, workKind: entry.workKind }
9297
+ });
9298
+ await apiClient.sendRunnerHeartbeat(entry.projectId, entry.runnerId, entry.repositoryLinkId, "online", {
9299
+ ...runnerHeartbeatMetadata(),
9300
+ preferenceMessage: "Pending result finalization replayed."
9301
+ }).catch(() => void 0);
9302
+ }
9303
+ return { status: "completed", workItem, response };
9304
+ }
9305
+ async function submitDurableResultMutation(apiClient, entry) {
9306
+ if (entry.resultKind === "assistantResult") {
9307
+ return apiClient.submitAssistantResult(entry.projectId, entry.workItemId, entry.result);
9308
+ }
9309
+ if (entry.resultKind === "impactPreviewResult") {
9310
+ return apiClient.submitImpactPreviewResult(entry.projectId, entry.workItemId, entry.result);
9311
+ }
9312
+ if (entry.resultKind === "issueDiagnosisResult") {
9313
+ return apiClient.submitIssueDiagnosisResult(entry.projectId, entry.workItemId, entry.result);
9314
+ }
9315
+ if (entry.resultKind === "securityPostureScanResult") {
9316
+ return apiClient.submitSecurityPostureScanResult(entry.projectId, entry.workItemId, entry.result);
9317
+ }
9318
+ if (entry.resultKind === "appEvaluationScanResult") {
9319
+ return apiClient.submitAppEvaluationScanResult(entry.projectId, entry.workItemId, entry.result);
9320
+ }
9321
+ if (entry.resultKind === "projectContextRefreshResult") {
9322
+ return apiClient.submitProjectContextRefreshResult(entry.projectId, entry.workItemId, entry.result);
9323
+ }
9324
+ if (entry.resultKind === "testQualityScanResult") {
9325
+ return apiClient.submitTestQualityScanResult(entry.projectId, entry.workItemId, entry.result);
9326
+ }
9327
+ if (entry.resultKind === "implementationTestGateResult") {
9328
+ return apiClient.submitImplementationTestGateResult(entry.projectId, entry.workItemId, entry.result);
9329
+ }
9330
+ return apiClient.submitImplementationVerificationResult(entry.projectId, entry.workItemId, entry.result);
9331
+ }
9332
+ function durableResultResponseWorkItem(response) {
9333
+ if (response && typeof response === "object" && "workItem" in response) {
9334
+ return response.workItem;
9335
+ }
9336
+ throw new Error("Runner result finalization response did not include a work item.");
9337
+ }
9338
+ function runnerResultFinalizationLabel(entry) {
9339
+ if (entry.workKind === "appEvaluationScan") return "app evaluation scan";
9340
+ if (entry.workKind === "securityPostureScan") return "security posture scan";
9341
+ if (entry.workKind === "projectContextRefresh") return "project context refresh";
9342
+ if (entry.workKind === "implementationVerification") return "implementation verification";
9343
+ if (entry.workKind === "testQualityScan") return "test quality scan";
9344
+ if (entry.workKind === "implementationTestGate") return "implementation test gate";
9345
+ if (entry.workKind === "issueDiagnosis") return "issue diagnosis";
9346
+ if (entry.workKind === "impactPreview") return "impact preview";
9347
+ return "assistant answer";
9348
+ }
9349
+ async function submitStagedDurableResultFinalization(apiClient, input) {
9350
+ const entry = createDurableResultFinalizationEntry(input);
9351
+ await upsertDurableResultFinalizationEntry(entry);
9352
+ return submitDurableResultFinalizationEntry(apiClient, entry);
9353
+ }
9354
+ async function submitPrimaryRunnerResult(apiClient, input) {
9355
+ const replay = await submitStagedDurableResultFinalization(apiClient, input);
9356
+ if (replay.status === "failed") {
9357
+ if (!replay.retryable) {
9358
+ throw new Error(replay.message);
9359
+ }
9360
+ return void 0;
9361
+ }
9362
+ return replay.response;
9363
+ }
9364
+ function pendingSessionTelemetry(sessionContext) {
9365
+ return {
9366
+ sessionPolicy: sessionContext.policy,
9367
+ sessionDecision: sessionContext.decision,
9368
+ sessionDecisionReason: sessionContext.reason,
9369
+ ...sessionContext.toolSession ? { toolSessionId: sessionContext.toolSession.toolSessionId } : {},
9370
+ ...sessionContext.toolSession?.sessionGroupKey ? { sessionGroupKey: sessionContext.toolSession.sessionGroupKey } : {}
9371
+ };
9372
+ }
9373
+ async function finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status, toolResult, workItemId }) {
9374
+ const settlements = await Promise.allSettled([
9375
+ finalizeToolSession({
9376
+ apiClient,
9377
+ projectId,
9378
+ status,
9379
+ runnerId,
9380
+ workItemId,
9381
+ stdout: toolResult.stdout,
9382
+ ...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
9383
+ ...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
9384
+ ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
9385
+ ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
9386
+ ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
9387
+ })
9388
+ ]);
9389
+ logRejectedSettlements("finalize runner result tool session", settlements);
9390
+ }
7991
9391
  async function finalizeBrainGenerationWork({
7992
9392
  apiClient,
7993
9393
  durationMs,
@@ -8145,28 +9545,9 @@ ${toolResult.stderr}`);
8145
9545
  answerError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8146
9546
  }
8147
9547
  const finalStatus = answerResult ? "completed" : "failed";
8148
- const updatedToolSession = await finalizeToolSession({
8149
- apiClient,
8150
- projectId,
8151
- status: finalStatus,
8152
- runnerId,
8153
- workItemId: workItem.workItemId,
8154
- stdout: toolResult.stdout,
8155
- ...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
8156
- ...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
8157
- ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
8158
- ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
8159
- ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
8160
- });
8161
- const sessionTelemetry = {
8162
- sessionPolicy: sessionContext.policy,
8163
- sessionDecision: sessionContext.decision,
8164
- sessionDecisionReason: sessionContext.reason,
8165
- ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
8166
- ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
8167
- };
9548
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8168
9549
  if (answerResult) {
8169
- const result = await apiClient.submitAssistantResult(projectId, workItem.workItemId, {
9550
+ const resultMutation = {
8170
9551
  status: "completed",
8171
9552
  runnerId,
8172
9553
  idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID()}`,
@@ -8177,7 +9558,21 @@ ${toolResult.stderr}`);
8177
9558
  durationMs,
8178
9559
  ...sessionTelemetry,
8179
9560
  message: `${toolName} returned a project knowledge answer.`
9561
+ };
9562
+ const result = await submitPrimaryRunnerResult(apiClient, {
9563
+ accountId: workItem.accountId,
9564
+ projectId,
9565
+ repositoryLinkId,
9566
+ runnerId,
9567
+ workItemId: workItem.workItemId,
9568
+ workKind: "assistantQuestion",
9569
+ resultKind: "assistantResult",
9570
+ attempt: workItem.attempt,
9571
+ idempotencyKey: resultMutation.idempotencyKey,
9572
+ result: resultMutation
8180
9573
  });
9574
+ if (!result) return { status: "failed", exitCode: 1 };
9575
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8181
9576
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8182
9577
  status: "completed",
8183
9578
  summary: `${toolName} returned a project knowledge answer.`,
@@ -8188,7 +9583,7 @@ ${toolResult.stderr}`);
8188
9583
  console.log("Project knowledge answer returned.");
8189
9584
  return { status: "completed", exitCode: 0 };
8190
9585
  }
8191
- const failedResult = await apiClient.submitAssistantResult(projectId, workItem.workItemId, {
9586
+ const failedMutation = {
8192
9587
  status: "failed",
8193
9588
  runnerId,
8194
9589
  idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID()}`,
@@ -8197,7 +9592,21 @@ ${toolResult.stderr}`);
8197
9592
  ...sessionTelemetry,
8198
9593
  message: `${toolName} did not produce a valid project knowledge answer.`,
8199
9594
  ...answerError ? { error: answerError } : {}
9595
+ };
9596
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
9597
+ accountId: workItem.accountId,
9598
+ projectId,
9599
+ repositoryLinkId,
9600
+ runnerId,
9601
+ workItemId: workItem.workItemId,
9602
+ workKind: "assistantQuestion",
9603
+ resultKind: "assistantResult",
9604
+ attempt: workItem.attempt,
9605
+ idempotencyKey: failedMutation.idempotencyKey,
9606
+ result: failedMutation
8200
9607
  });
9608
+ if (!failedResult) return { status: "failed", exitCode: 1 };
9609
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8201
9610
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8202
9611
  status: "failed",
8203
9612
  summary: answerError ?? `${toolName} did not produce a valid project knowledge answer.`,
@@ -8234,29 +9643,10 @@ ${toolResult.stderr}`);
8234
9643
  previewError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8235
9644
  }
8236
9645
  const finalStatus = report ? "completed" : "failed";
8237
- const updatedToolSession = await finalizeToolSession({
8238
- apiClient,
8239
- projectId,
8240
- status: finalStatus,
8241
- runnerId,
8242
- workItemId: workItem.workItemId,
8243
- stdout: toolResult.stdout,
8244
- ...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
8245
- ...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
8246
- ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
8247
- ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
8248
- ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
8249
- });
8250
- const sessionTelemetry = {
8251
- sessionPolicy: sessionContext.policy,
8252
- sessionDecision: sessionContext.decision,
8253
- sessionDecisionReason: sessionContext.reason,
8254
- ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
8255
- ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
8256
- };
9646
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8257
9647
  if (report) {
8258
9648
  const metadata = await readProjectLink(root).catch(() => void 0);
8259
- const result = await apiClient.submitImpactPreviewResult(projectId, workItem.workItemId, {
9649
+ const resultMutation = {
8260
9650
  status: "completed",
8261
9651
  runnerId,
8262
9652
  idempotencyKey: `impact_${workItem.workItemId}_${randomUUID()}`,
@@ -8268,7 +9658,21 @@ ${toolResult.stderr}`);
8268
9658
  durationMs,
8269
9659
  ...sessionTelemetry,
8270
9660
  message: `${toolName} returned an implementation impact preview.`
9661
+ };
9662
+ const result = await submitPrimaryRunnerResult(apiClient, {
9663
+ accountId: workItem.accountId,
9664
+ projectId,
9665
+ repositoryLinkId,
9666
+ runnerId,
9667
+ workItemId: workItem.workItemId,
9668
+ workKind: "impactPreview",
9669
+ resultKind: "impactPreviewResult",
9670
+ attempt: workItem.attempt,
9671
+ idempotencyKey: resultMutation.idempotencyKey,
9672
+ result: resultMutation
8271
9673
  });
9674
+ if (!result) return { status: "failed", exitCode: 1 };
9675
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8272
9676
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8273
9677
  status: "completed",
8274
9678
  summary: `${toolName} returned an implementation impact preview.`,
@@ -8279,7 +9683,7 @@ ${toolResult.stderr}`);
8279
9683
  console.log(result.implementationWorkItem ? "Impact preview returned; implementation work is now queued." : "Impact preview returned.");
8280
9684
  return { status: "completed", exitCode: 0 };
8281
9685
  }
8282
- const failedResult = await apiClient.submitImpactPreviewResult(projectId, workItem.workItemId, {
9686
+ const failedMutation = {
8283
9687
  status: "failed",
8284
9688
  runnerId,
8285
9689
  idempotencyKey: `impact_${workItem.workItemId}_${randomUUID()}`,
@@ -8288,7 +9692,21 @@ ${toolResult.stderr}`);
8288
9692
  ...sessionTelemetry,
8289
9693
  message: `${toolName} did not produce a valid impact preview.`,
8290
9694
  ...previewError ? { error: previewError } : {}
9695
+ };
9696
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
9697
+ accountId: workItem.accountId,
9698
+ projectId,
9699
+ repositoryLinkId,
9700
+ runnerId,
9701
+ workItemId: workItem.workItemId,
9702
+ workKind: "impactPreview",
9703
+ resultKind: "impactPreviewResult",
9704
+ attempt: workItem.attempt,
9705
+ idempotencyKey: failedMutation.idempotencyKey,
9706
+ result: failedMutation
8291
9707
  });
9708
+ if (!failedResult) return { status: "failed", exitCode: 1 };
9709
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8292
9710
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8293
9711
  status: "failed",
8294
9712
  summary: previewError ?? `${toolName} did not produce a valid impact preview.`,
@@ -8324,28 +9742,9 @@ ${toolResult.stderr}`);
8324
9742
  diagnosisError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8325
9743
  }
8326
9744
  const finalStatus = diagnosis ? "completed" : "failed";
8327
- const updatedToolSession = await finalizeToolSession({
8328
- apiClient,
8329
- projectId,
8330
- status: finalStatus,
8331
- runnerId,
8332
- workItemId: workItem.workItemId,
8333
- stdout: toolResult.stdout,
8334
- ...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
8335
- ...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
8336
- ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
8337
- ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
8338
- ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
8339
- });
8340
- const sessionTelemetry = {
8341
- sessionPolicy: sessionContext.policy,
8342
- sessionDecision: sessionContext.decision,
8343
- sessionDecisionReason: sessionContext.reason,
8344
- ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
8345
- ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
8346
- };
9745
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8347
9746
  if (diagnosis) {
8348
- const result = await apiClient.submitIssueDiagnosisResult(projectId, workItem.workItemId, {
9747
+ const resultMutation = {
8349
9748
  status: "completed",
8350
9749
  runnerId,
8351
9750
  idempotencyKey: `issue_${workItem.workItemId}_${randomUUID()}`,
@@ -8354,7 +9753,21 @@ ${toolResult.stderr}`);
8354
9753
  durationMs,
8355
9754
  ...sessionTelemetry,
8356
9755
  message: `${toolName} returned an issue root-cause analysis.`
9756
+ };
9757
+ const result = await submitPrimaryRunnerResult(apiClient, {
9758
+ accountId: workItem.accountId,
9759
+ projectId,
9760
+ repositoryLinkId,
9761
+ runnerId,
9762
+ workItemId: workItem.workItemId,
9763
+ workKind: "issueDiagnosis",
9764
+ resultKind: "issueDiagnosisResult",
9765
+ attempt: workItem.attempt,
9766
+ idempotencyKey: resultMutation.idempotencyKey,
9767
+ result: resultMutation
8357
9768
  });
9769
+ if (!result) return { status: "failed", exitCode: 1 };
9770
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8358
9771
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8359
9772
  status: "completed",
8360
9773
  summary: `${toolName} returned an issue root-cause analysis.`,
@@ -8365,7 +9778,7 @@ ${toolResult.stderr}`);
8365
9778
  console.log("Issue diagnosis returned for approval.");
8366
9779
  return { status: "completed", exitCode: 0 };
8367
9780
  }
8368
- const failedResult = await apiClient.submitIssueDiagnosisResult(projectId, workItem.workItemId, {
9781
+ const failedMutation = {
8369
9782
  status: "failed",
8370
9783
  runnerId,
8371
9784
  idempotencyKey: `issue_${workItem.workItemId}_${randomUUID()}`,
@@ -8374,7 +9787,21 @@ ${toolResult.stderr}`);
8374
9787
  ...sessionTelemetry,
8375
9788
  message: `${toolName} did not produce a valid issue diagnosis.`,
8376
9789
  ...diagnosisError ? { error: diagnosisError } : {}
9790
+ };
9791
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
9792
+ accountId: workItem.accountId,
9793
+ projectId,
9794
+ repositoryLinkId,
9795
+ runnerId,
9796
+ workItemId: workItem.workItemId,
9797
+ workKind: "issueDiagnosis",
9798
+ resultKind: "issueDiagnosisResult",
9799
+ attempt: workItem.attempt,
9800
+ idempotencyKey: failedMutation.idempotencyKey,
9801
+ result: failedMutation
8377
9802
  });
9803
+ if (!failedResult) return { status: "failed", exitCode: 1 };
9804
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8378
9805
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8379
9806
  status: "failed",
8380
9807
  summary: diagnosisError ?? `${toolName} did not produce a valid issue diagnosis.`,
@@ -8410,28 +9837,9 @@ ${toolResult.stderr}`);
8410
9837
  scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8411
9838
  }
8412
9839
  const finalStatus = scanResult ? "completed" : "failed";
8413
- const updatedToolSession = await finalizeToolSession({
8414
- apiClient,
8415
- projectId,
8416
- status: finalStatus,
8417
- runnerId,
8418
- workItemId: workItem.workItemId,
8419
- stdout: toolResult.stdout,
8420
- ...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
8421
- ...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
8422
- ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
8423
- ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
8424
- ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
8425
- });
8426
- const sessionTelemetry = {
8427
- sessionPolicy: sessionContext.policy,
8428
- sessionDecision: sessionContext.decision,
8429
- sessionDecisionReason: sessionContext.reason,
8430
- ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
8431
- ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
8432
- };
9840
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8433
9841
  if (scanResult) {
8434
- const result = await apiClient.submitSecurityPostureScanResult(projectId, workItem.workItemId, {
9842
+ const resultMutation = {
8435
9843
  status: "completed",
8436
9844
  runnerId,
8437
9845
  idempotencyKey: `security_${workItem.workItemId}_${randomUUID()}`,
@@ -8440,7 +9848,21 @@ ${toolResult.stderr}`);
8440
9848
  durationMs,
8441
9849
  ...sessionTelemetry,
8442
9850
  message: `${toolName} returned a security posture scan.`
9851
+ };
9852
+ const result = await submitPrimaryRunnerResult(apiClient, {
9853
+ accountId: workItem.accountId,
9854
+ projectId,
9855
+ repositoryLinkId,
9856
+ runnerId,
9857
+ workItemId: workItem.workItemId,
9858
+ workKind: "securityPostureScan",
9859
+ resultKind: "securityPostureScanResult",
9860
+ attempt: workItem.attempt,
9861
+ idempotencyKey: resultMutation.idempotencyKey,
9862
+ result: resultMutation
8443
9863
  });
9864
+ if (!result) return { status: "failed", exitCode: 1 };
9865
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8444
9866
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8445
9867
  status: "completed",
8446
9868
  summary: `${toolName} returned a security posture scan.`,
@@ -8451,7 +9873,7 @@ ${toolResult.stderr}`);
8451
9873
  console.log("Security posture scan returned for review.");
8452
9874
  return { status: "completed", exitCode: 0 };
8453
9875
  }
8454
- const failedResult = await apiClient.submitSecurityPostureScanResult(projectId, workItem.workItemId, {
9876
+ const failedMutation = {
8455
9877
  status: "failed",
8456
9878
  runnerId,
8457
9879
  idempotencyKey: `security_${workItem.workItemId}_${randomUUID()}`,
@@ -8460,7 +9882,21 @@ ${toolResult.stderr}`);
8460
9882
  ...sessionTelemetry,
8461
9883
  message: `${toolName} did not produce a valid security posture scan.`,
8462
9884
  ...scanError ? { error: scanError } : {}
9885
+ };
9886
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
9887
+ accountId: workItem.accountId,
9888
+ projectId,
9889
+ repositoryLinkId,
9890
+ runnerId,
9891
+ workItemId: workItem.workItemId,
9892
+ workKind: "securityPostureScan",
9893
+ resultKind: "securityPostureScanResult",
9894
+ attempt: workItem.attempt,
9895
+ idempotencyKey: failedMutation.idempotencyKey,
9896
+ result: failedMutation
8463
9897
  });
9898
+ if (!failedResult) return { status: "failed", exitCode: 1 };
9899
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8464
9900
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8465
9901
  status: "failed",
8466
9902
  summary: scanError ?? `${toolName} did not produce a valid security posture scan.`,
@@ -8496,28 +9932,9 @@ ${toolResult.stderr}`);
8496
9932
  scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8497
9933
  }
8498
9934
  const finalStatus = scanResult ? "completed" : "failed";
8499
- const updatedToolSession = await finalizeToolSession({
8500
- apiClient,
8501
- projectId,
8502
- status: finalStatus,
8503
- runnerId,
8504
- workItemId: workItem.workItemId,
8505
- stdout: toolResult.stdout,
8506
- ...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
8507
- ...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
8508
- ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
8509
- ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
8510
- ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
8511
- });
8512
- const sessionTelemetry = {
8513
- sessionPolicy: sessionContext.policy,
8514
- sessionDecision: sessionContext.decision,
8515
- sessionDecisionReason: sessionContext.reason,
8516
- ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
8517
- ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
8518
- };
9935
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8519
9936
  if (scanResult) {
8520
- const result = await apiClient.submitAppEvaluationScanResult(projectId, workItem.workItemId, {
9937
+ const resultMutation = {
8521
9938
  status: "completed",
8522
9939
  runnerId,
8523
9940
  idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID()}`,
@@ -8526,7 +9943,21 @@ ${toolResult.stderr}`);
8526
9943
  durationMs,
8527
9944
  ...sessionTelemetry,
8528
9945
  message: `${toolName} returned an app evaluation scan.`
9946
+ };
9947
+ const result = await submitPrimaryRunnerResult(apiClient, {
9948
+ accountId: workItem.accountId,
9949
+ projectId,
9950
+ repositoryLinkId,
9951
+ runnerId,
9952
+ workItemId: workItem.workItemId,
9953
+ workKind: "appEvaluationScan",
9954
+ resultKind: "appEvaluationScanResult",
9955
+ attempt: workItem.attempt,
9956
+ idempotencyKey: resultMutation.idempotencyKey,
9957
+ result: resultMutation
8529
9958
  });
9959
+ if (!result) return { status: "failed", exitCode: 1 };
9960
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8530
9961
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8531
9962
  status: "completed",
8532
9963
  summary: `${toolName} returned an app evaluation scan.`,
@@ -8537,7 +9968,7 @@ ${toolResult.stderr}`);
8537
9968
  console.log("App evaluation scan returned for review.");
8538
9969
  return { status: "completed", exitCode: 0 };
8539
9970
  }
8540
- const failedResult = await apiClient.submitAppEvaluationScanResult(projectId, workItem.workItemId, {
9971
+ const failedMutation = {
8541
9972
  status: "failed",
8542
9973
  runnerId,
8543
9974
  idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID()}`,
@@ -8546,7 +9977,21 @@ ${toolResult.stderr}`);
8546
9977
  ...sessionTelemetry,
8547
9978
  message: `${toolName} did not produce a valid app evaluation scan.`,
8548
9979
  ...scanError ? { error: scanError } : {}
9980
+ };
9981
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
9982
+ accountId: workItem.accountId,
9983
+ projectId,
9984
+ repositoryLinkId,
9985
+ runnerId,
9986
+ workItemId: workItem.workItemId,
9987
+ workKind: "appEvaluationScan",
9988
+ resultKind: "appEvaluationScanResult",
9989
+ attempt: workItem.attempt,
9990
+ idempotencyKey: failedMutation.idempotencyKey,
9991
+ result: failedMutation
8549
9992
  });
9993
+ if (!failedResult) return { status: "failed", exitCode: 1 };
9994
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8550
9995
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8551
9996
  status: "failed",
8552
9997
  summary: scanError ?? `${toolName} did not produce a valid app evaluation scan.`,
@@ -8583,28 +10028,9 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
8583
10028
  refreshError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8584
10029
  }
8585
10030
  const finalStatus = refreshResult ? "completed" : "failed";
8586
- const updatedToolSession = await finalizeToolSession({
8587
- apiClient,
8588
- projectId,
8589
- status: finalStatus,
8590
- runnerId,
8591
- workItemId: workItem.workItemId,
8592
- stdout: toolResult.stdout,
8593
- ...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
8594
- ...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
8595
- ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
8596
- ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
8597
- ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
8598
- });
8599
- const sessionTelemetry = {
8600
- sessionPolicy: sessionContext.policy,
8601
- sessionDecision: sessionContext.decision,
8602
- sessionDecisionReason: sessionContext.reason,
8603
- ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
8604
- ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
8605
- };
10031
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8606
10032
  if (refreshResult) {
8607
- const result = await apiClient.submitProjectContextRefreshResult(projectId, workItem.workItemId, {
10033
+ const resultMutation = {
8608
10034
  status: "completed",
8609
10035
  runnerId,
8610
10036
  idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID()}`,
@@ -8613,7 +10039,21 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
8613
10039
  durationMs,
8614
10040
  ...sessionTelemetry,
8615
10041
  message: `${toolName} returned a project context refresh.`
10042
+ };
10043
+ const result = await submitPrimaryRunnerResult(apiClient, {
10044
+ accountId: workItem.accountId,
10045
+ projectId,
10046
+ repositoryLinkId,
10047
+ runnerId,
10048
+ workItemId: workItem.workItemId,
10049
+ workKind: "projectContextRefresh",
10050
+ resultKind: "projectContextRefreshResult",
10051
+ attempt: workItem.attempt,
10052
+ idempotencyKey: resultMutation.idempotencyKey,
10053
+ result: resultMutation
8616
10054
  });
10055
+ if (!result) return { status: "failed", exitCode: 1 };
10056
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8617
10057
  const failureSummary = projectContextRefreshSubmissionFailureSummary(result);
8618
10058
  if (failureSummary) {
8619
10059
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
@@ -8636,7 +10076,7 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
8636
10076
  console.log("Project context refresh returned for review.");
8637
10077
  return { status: "completed", exitCode: 0 };
8638
10078
  }
8639
- const failedResult = await apiClient.submitProjectContextRefreshResult(projectId, workItem.workItemId, {
10079
+ const failedMutation = {
8640
10080
  status: "failed",
8641
10081
  runnerId,
8642
10082
  idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID()}`,
@@ -8645,7 +10085,21 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
8645
10085
  ...sessionTelemetry,
8646
10086
  message: `${toolName} did not produce a valid project context refresh.`,
8647
10087
  ...refreshError ? { error: refreshError } : {}
10088
+ };
10089
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
10090
+ accountId: workItem.accountId,
10091
+ projectId,
10092
+ repositoryLinkId,
10093
+ runnerId,
10094
+ workItemId: workItem.workItemId,
10095
+ workKind: "projectContextRefresh",
10096
+ resultKind: "projectContextRefreshResult",
10097
+ attempt: workItem.attempt,
10098
+ idempotencyKey: failedMutation.idempotencyKey,
10099
+ result: failedMutation
8648
10100
  });
10101
+ if (!failedResult) return { status: "failed", exitCode: 1 };
10102
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8649
10103
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8650
10104
  status: "failed",
8651
10105
  summary: refreshError ?? `${toolName} did not produce a valid project context refresh.`,
@@ -8681,28 +10135,9 @@ ${toolResult.stderr}`);
8681
10135
  verificationError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8682
10136
  }
8683
10137
  const finalStatus = verificationResult ? "completed" : "failed";
8684
- const updatedToolSession = await finalizeToolSession({
8685
- apiClient,
8686
- projectId,
8687
- status: finalStatus,
8688
- runnerId,
8689
- workItemId: workItem.workItemId,
8690
- stdout: toolResult.stdout,
8691
- ...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
8692
- ...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
8693
- ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
8694
- ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
8695
- ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
8696
- });
8697
- const sessionTelemetry = {
8698
- sessionPolicy: sessionContext.policy,
8699
- sessionDecision: sessionContext.decision,
8700
- sessionDecisionReason: sessionContext.reason,
8701
- ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
8702
- ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
8703
- };
10138
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8704
10139
  if (verificationResult) {
8705
- const result = await apiClient.submitImplementationVerificationResult(projectId, workItem.workItemId, {
10140
+ const resultMutation = {
8706
10141
  status: "completed",
8707
10142
  runnerId,
8708
10143
  idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID()}`,
@@ -8711,7 +10146,21 @@ ${toolResult.stderr}`);
8711
10146
  durationMs,
8712
10147
  ...sessionTelemetry,
8713
10148
  message: `${toolName} returned implementation verification proof.`
10149
+ };
10150
+ const result = await submitPrimaryRunnerResult(apiClient, {
10151
+ accountId: workItem.accountId,
10152
+ projectId,
10153
+ repositoryLinkId,
10154
+ runnerId,
10155
+ workItemId: workItem.workItemId,
10156
+ workKind: "implementationVerification",
10157
+ resultKind: "implementationVerificationResult",
10158
+ attempt: workItem.attempt,
10159
+ idempotencyKey: resultMutation.idempotencyKey,
10160
+ result: resultMutation
8714
10161
  });
10162
+ if (!result) return { status: "failed", exitCode: 1 };
10163
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8715
10164
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8716
10165
  status: verificationResult.outcome === "verifiedImplemented" ? "completed" : verificationResult.outcome === "verificationBlocked" ? "blocked" : "warning",
8717
10166
  summary: verificationResult.summary,
@@ -8722,7 +10171,7 @@ ${toolResult.stderr}`);
8722
10171
  console.log("Implementation verification returned for review.");
8723
10172
  return { status: "completed", exitCode: 0 };
8724
10173
  }
8725
- const failedResult = await apiClient.submitImplementationVerificationResult(projectId, workItem.workItemId, {
10174
+ const failedMutation = {
8726
10175
  status: "failed",
8727
10176
  runnerId,
8728
10177
  idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID()}`,
@@ -8731,7 +10180,21 @@ ${toolResult.stderr}`);
8731
10180
  ...sessionTelemetry,
8732
10181
  message: `${toolName} did not produce valid implementation verification proof.`,
8733
10182
  ...verificationError ? { error: verificationError } : {}
10183
+ };
10184
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
10185
+ accountId: workItem.accountId,
10186
+ projectId,
10187
+ repositoryLinkId,
10188
+ runnerId,
10189
+ workItemId: workItem.workItemId,
10190
+ workKind: "implementationVerification",
10191
+ resultKind: "implementationVerificationResult",
10192
+ attempt: workItem.attempt,
10193
+ idempotencyKey: failedMutation.idempotencyKey,
10194
+ result: failedMutation
8734
10195
  });
10196
+ if (!failedResult) return { status: "failed", exitCode: 1 };
10197
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8735
10198
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8736
10199
  status: "failed",
8737
10200
  summary: verificationError ?? `${toolName} did not produce valid implementation verification proof.`,
@@ -8742,6 +10205,196 @@ ${toolResult.stderr}`);
8742
10205
  console.error(verificationError ?? "Local runner implementation verification failed.");
8743
10206
  return { status: "failed", exitCode: toolResult.exitCode || 1 };
8744
10207
  }
10208
+ async function finalizeTestQualityScanWork({
10209
+ apiClient,
10210
+ durationMs,
10211
+ projectId,
10212
+ repositoryLinkId,
10213
+ runnerId,
10214
+ sessionContext,
10215
+ toolConfig,
10216
+ toolName,
10217
+ toolResult,
10218
+ workItem
10219
+ }) {
10220
+ let scanResult = void 0;
10221
+ let scanError;
10222
+ if (toolResult.exitCode === 0) {
10223
+ try {
10224
+ scanResult = parseTestQualityScanResult(`${toolResult.stdout}
10225
+ ${toolResult.stderr}`);
10226
+ } catch (error) {
10227
+ scanError = errorMessage3(error);
10228
+ }
10229
+ } else {
10230
+ scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
10231
+ }
10232
+ const finalStatus = scanResult ? "completed" : "failed";
10233
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
10234
+ if (scanResult) {
10235
+ const resultMutation = {
10236
+ status: "completed",
10237
+ runnerId,
10238
+ idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID()}`,
10239
+ result: scanResult,
10240
+ tool: toolName,
10241
+ durationMs,
10242
+ ...sessionTelemetry,
10243
+ message: `${toolName} returned a test quality scan.`
10244
+ };
10245
+ const result = await submitPrimaryRunnerResult(apiClient, {
10246
+ accountId: workItem.accountId,
10247
+ projectId,
10248
+ repositoryLinkId,
10249
+ runnerId,
10250
+ workItemId: workItem.workItemId,
10251
+ workKind: "testQualityScan",
10252
+ resultKind: "testQualityScanResult",
10253
+ attempt: workItem.attempt,
10254
+ idempotencyKey: resultMutation.idempotencyKey,
10255
+ result: resultMutation
10256
+ });
10257
+ if (!result) return { status: "failed", exitCode: 1 };
10258
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10259
+ await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10260
+ status: scanResult.findings.some((finding) => finding.severity === "critical" || finding.severity === "high") ? "warning" : "completed",
10261
+ summary: scanResult.summary,
10262
+ idempotencyKey: `runner_milestone_test_quality_completed_${workItem.workItemId}_${result.workItem.idempotencyKey}`,
10263
+ metadata: { tool: toolName, durationMs, findingCount: scanResult.findings.length, commandCount: scanResult.commandSummaries.length, verificationSummary: scanResult.verificationPlan.join(" | ") }
10264
+ });
10265
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
10266
+ console.log("Test quality scan returned for review.");
10267
+ return { status: "completed", exitCode: 0 };
10268
+ }
10269
+ const failedMutation = {
10270
+ status: "failed",
10271
+ runnerId,
10272
+ idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID()}`,
10273
+ tool: toolName,
10274
+ durationMs,
10275
+ ...sessionTelemetry,
10276
+ message: `${toolName} did not produce a valid test quality scan.`,
10277
+ ...scanError ? { error: scanError } : {}
10278
+ };
10279
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
10280
+ accountId: workItem.accountId,
10281
+ projectId,
10282
+ repositoryLinkId,
10283
+ runnerId,
10284
+ workItemId: workItem.workItemId,
10285
+ workKind: "testQualityScan",
10286
+ resultKind: "testQualityScanResult",
10287
+ attempt: workItem.attempt,
10288
+ idempotencyKey: failedMutation.idempotencyKey,
10289
+ result: failedMutation
10290
+ });
10291
+ if (!failedResult) return { status: "failed", exitCode: 1 };
10292
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10293
+ await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10294
+ status: "failed",
10295
+ summary: scanError ?? `${toolName} did not produce a valid test quality scan.`,
10296
+ idempotencyKey: `runner_milestone_test_quality_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
10297
+ metadata: { tool: toolName, durationMs, verificationSummary: "Test quality output did not include valid structured JSON." }
10298
+ });
10299
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
10300
+ console.error(scanError ?? "Local runner test quality scan failed.");
10301
+ return { status: "failed", exitCode: toolResult.exitCode || 1 };
10302
+ }
10303
+ async function finalizeImplementationTestGateWork({
10304
+ apiClient,
10305
+ durationMs,
10306
+ projectId,
10307
+ repositoryLinkId,
10308
+ runnerId,
10309
+ sessionContext,
10310
+ toolConfig,
10311
+ toolName,
10312
+ toolResult,
10313
+ workItem
10314
+ }) {
10315
+ let gateResult = void 0;
10316
+ let gateError;
10317
+ if (toolResult.exitCode === 0) {
10318
+ try {
10319
+ gateResult = parseImplementationTestGateResult(`${toolResult.stdout}
10320
+ ${toolResult.stderr}`);
10321
+ } catch (error) {
10322
+ gateError = errorMessage3(error);
10323
+ }
10324
+ } else {
10325
+ gateError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
10326
+ }
10327
+ const finalStatus = gateResult ? "completed" : "failed";
10328
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
10329
+ if (gateResult) {
10330
+ const resultMutation = {
10331
+ status: "completed",
10332
+ runnerId,
10333
+ idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID()}`,
10334
+ result: gateResult,
10335
+ tool: toolName,
10336
+ durationMs,
10337
+ ...sessionTelemetry,
10338
+ message: `${toolName} returned an implementation test gate result.`
10339
+ };
10340
+ const result = await submitPrimaryRunnerResult(apiClient, {
10341
+ accountId: workItem.accountId,
10342
+ projectId,
10343
+ repositoryLinkId,
10344
+ runnerId,
10345
+ workItemId: workItem.workItemId,
10346
+ workKind: "implementationTestGate",
10347
+ resultKind: "implementationTestGateResult",
10348
+ attempt: workItem.attempt,
10349
+ idempotencyKey: resultMutation.idempotencyKey,
10350
+ result: resultMutation
10351
+ });
10352
+ if (!result) return { status: "failed", exitCode: 1 };
10353
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10354
+ await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10355
+ status: gateResult.outcome === "passed" ? "completed" : gateResult.outcome === "blocked" ? "blocked" : "failed",
10356
+ summary: gateResult.summary,
10357
+ idempotencyKey: `runner_milestone_implementation_test_gate_completed_${workItem.workItemId}_${result.workItem.idempotencyKey}`,
10358
+ metadata: { tool: toolName, durationMs, outcome: gateResult.outcome, findingCount: gateResult.findings.length, commandCount: gateResult.commandSummaries.length, verificationSummary: gateResult.verificationPlan.join(" | ") }
10359
+ });
10360
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
10361
+ console.log("Implementation test gate returned for review.");
10362
+ return { status: gateResult.outcome === "passed" ? "completed" : "failed", exitCode: gateResult.outcome === "passed" ? 0 : 1 };
10363
+ }
10364
+ const failedMutation = {
10365
+ status: "failed",
10366
+ runnerId,
10367
+ idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID()}`,
10368
+ tool: toolName,
10369
+ durationMs,
10370
+ ...sessionTelemetry,
10371
+ message: `${toolName} did not produce a valid implementation test gate result.`,
10372
+ ...gateError ? { error: gateError } : {}
10373
+ };
10374
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
10375
+ accountId: workItem.accountId,
10376
+ projectId,
10377
+ repositoryLinkId,
10378
+ runnerId,
10379
+ workItemId: workItem.workItemId,
10380
+ workKind: "implementationTestGate",
10381
+ resultKind: "implementationTestGateResult",
10382
+ attempt: workItem.attempt,
10383
+ idempotencyKey: failedMutation.idempotencyKey,
10384
+ result: failedMutation
10385
+ });
10386
+ if (!failedResult) return { status: "failed", exitCode: 1 };
10387
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10388
+ await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10389
+ status: "failed",
10390
+ summary: gateError ?? `${toolName} did not produce a valid implementation test gate result.`,
10391
+ idempotencyKey: `runner_milestone_implementation_test_gate_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
10392
+ metadata: { tool: toolName, durationMs, verificationSummary: "Implementation test gate output did not include valid structured JSON." }
10393
+ });
10394
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
10395
+ console.error(gateError ?? "Local runner implementation test gate failed.");
10396
+ return { status: "failed", exitCode: toolResult.exitCode || 1 };
10397
+ }
8745
10398
  async function createRunnerWorkPrompt(apiClient, projectId, workItem) {
8746
10399
  if (workItem.workKind === "assistantQuestion") {
8747
10400
  const emptyProjectContext = { maps: [], refreshes: [], misses: [] };
@@ -8798,6 +10451,15 @@ async function createRunnerWorkPrompt(apiClient, projectId, workItem) {
8798
10451
  appEvaluationScan: { documents: documents2 }
8799
10452
  });
8800
10453
  }
10454
+ if (workItem.workKind === "testQualityScan") {
10455
+ const { documents: documents2 } = await apiClient.listBrainDocuments(projectId);
10456
+ return createWorkExecutionPrompt(workItem, {
10457
+ testQualityScan: { documents: documents2 }
10458
+ });
10459
+ }
10460
+ if (workItem.workKind === "implementationTestGate") {
10461
+ return createWorkExecutionPrompt(workItem);
10462
+ }
8801
10463
  if (workItem.workKind === "projectContextRefresh") {
8802
10464
  const emptyProjectContext = { maps: [], refreshes: [], misses: [] };
8803
10465
  const [{ documents: documents2 }, context] = await Promise.all([
@@ -9285,15 +10947,17 @@ function toRunnerToolCapabilities(tools) {
9285
10947
  supportsGitWorktreeIsolation: true
9286
10948
  }));
9287
10949
  }
9288
- function runnerIsolationCapabilityMetadata() {
10950
+ function runnerIsolationCapabilityMetadata(laneMetadata = {}) {
9289
10951
  return {
9290
10952
  machineId: runnerMachineId(),
9291
10953
  supportedWorkKinds: runnerSupportedWorkKinds,
9292
10954
  supportsBranchIsolation: true,
9293
- supportsGitWorktreeIsolation: true
10955
+ supportsGitWorktreeIsolation: true,
10956
+ ...laneMetadata.claimLaneId ? { claimLaneId: laneMetadata.claimLaneId } : {},
10957
+ ...laneMetadata.maxConcurrentWork !== void 0 ? { maxConcurrentWork: laneMetadata.maxConcurrentWork } : {}
9294
10958
  };
9295
10959
  }
9296
- function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode()) {
10960
+ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode(), concurrencyMetadata = { maxConcurrentWork: 1, activeClaimLaneIds: ["default"] }) {
9297
10961
  const modelConfig = toolConfig ? toolConfigModelOptions(toolConfig) : {};
9298
10962
  const effectiveModelConfig = toolConfig?.ready ? modelConfig : {};
9299
10963
  return {
@@ -9301,6 +10965,8 @@ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode()) {
9301
10965
  mode,
9302
10966
  hostname: os8.hostname(),
9303
10967
  ...runnerIsolationCapabilityMetadata(),
10968
+ maxConcurrentWork: concurrencyMetadata.maxConcurrentWork,
10969
+ activeClaimLaneIds: concurrencyMetadata.activeClaimLaneIds,
9304
10970
  resourceUsage: sampleCurrentRunnerResourceUsage(),
9305
10971
  ...toolConfig?.capabilities ? { capabilities: toolConfig.capabilities } : {},
9306
10972
  ...toolConfig?.requestedTool ? { requestedTool: toolConfig.requestedTool } : {},