@amistio/cli 0.1.24 → 0.1.26

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,156 @@ 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
+ ]);
5455
+ var canonicalTestCommandKinds = /* @__PURE__ */ new Map([
5456
+ ["verify", "verify"],
5457
+ ["verification", "verify"],
5458
+ ["wholeapp", "verify"],
5459
+ ["wholeappverification", "verify"],
5460
+ ["full", "verify"],
5461
+ ["fullcheck", "verify"],
5462
+ ["fullchecks", "verify"],
5463
+ ["allcheck", "verify"],
5464
+ ["allchecks", "verify"],
5465
+ ["ci", "verify"],
5466
+ ["continuousintegration", "verify"],
5467
+ ["test", "test"],
5468
+ ["tests", "test"],
5469
+ ["testing", "test"],
5470
+ ["unittest", "test"],
5471
+ ["unittests", "test"],
5472
+ ["integrationtest", "test"],
5473
+ ["integrationtests", "test"],
5474
+ ["e2etest", "test"],
5475
+ ["e2etests", "test"],
5476
+ ["endtoend", "test"],
5477
+ ["endtoendtest", "test"],
5478
+ ["spec", "test"],
5479
+ ["specs", "test"],
5480
+ ["suite", "test"],
5481
+ ["testsuite", "test"],
5482
+ ["jest", "test"],
5483
+ ["vitest", "test"],
5484
+ ["coverage", "coverage"],
5485
+ ["coveragereport", "coverage"],
5486
+ ["lint", "lint"],
5487
+ ["linting", "lint"],
5488
+ ["eslint", "lint"],
5489
+ ["format", "lint"],
5490
+ ["formatting", "lint"],
5491
+ ["style", "lint"],
5492
+ ["quality", "lint"],
5493
+ ["qualitycheck", "lint"],
5494
+ ["staticanalysis", "lint"],
5495
+ ["typecheck", "typecheck"],
5496
+ ["typechecks", "typecheck"],
5497
+ ["typechecking", "typecheck"],
5498
+ ["typecheck", "typecheck"],
5499
+ ["tsc", "typecheck"],
5500
+ ["typescript", "typecheck"],
5501
+ ["build", "build"],
5502
+ ["compile", "build"],
5503
+ ["focused", "focused"],
5504
+ ["focus", "focused"],
5505
+ ["targeted", "focused"],
5506
+ ["scoped", "focused"],
5507
+ ["package", "focused"],
5508
+ ["packaged", "focused"],
5509
+ ["affected", "focused"],
5510
+ ["affectedtests", "focused"],
5511
+ ["touched", "focused"],
5512
+ ["touchedtests", "focused"],
5513
+ ["changed", "focused"],
5514
+ ["changedtests", "focused"],
5515
+ ["other", "focused"]
5516
+ ]);
4735
5517
  var canonicalProjectContextCitationSources = /* @__PURE__ */ new Map([
4736
5518
  ["projectbrain", "projectBrain"],
4737
5519
  ["approvedbrain", "projectBrain"],
@@ -4756,6 +5538,7 @@ function createImplementationVerificationPrompt(workItem) {
4756
5538
  `Project ID: ${workItem.projectId}`,
4757
5539
  `Implementation verification ID: ${workItem.implementationVerificationId ?? "unknown"}`,
4758
5540
  `Source work item ID: ${workItem.sourceWorkItemId ?? "unknown"}`,
5541
+ ...autopilotPromptLines(workItem),
4759
5542
  "",
4760
5543
  "## Verification Request",
4761
5544
  "",
@@ -4789,22 +5572,114 @@ function createImplementationVerificationPrompt(workItem) {
4789
5572
  "Do not put Markdown fences around the markers. Do not implement or fix anything during this verification pass."
4790
5573
  ].join("\n");
4791
5574
  }
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
- }
5575
+ function createTestQualityScanPrompt(workItem, context) {
5576
+ const approvedContext = (context?.documents ?? []).filter((document) => document.status === "approved" || document.syncState === "approved" || document.syncState === "synced").slice(0, 20).map((document) => [
5577
+ `### ${document.title}`,
5578
+ `documentId: ${document.documentId}`,
5579
+ `documentType: ${document.documentType}`,
5580
+ `repoPath: ${document.repoPath}`,
5581
+ `revision: ${document.revision}`,
5582
+ document.content.slice(0, 3e3)
5583
+ ].join("\n")).join("\n\n");
5584
+ return [
5585
+ "# Amistio Test Quality Scan",
5586
+ "",
5587
+ "You are running locally through the Amistio CLI inside the user's repository.",
5588
+ "Run a non-mutating daily test, coverage, and quality scan for this repository.",
5589
+ "Do not modify files, create branches, commit, push, install packages, run implementation prompts, or make unaudited network calls.",
5590
+ "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.",
5591
+ "Return missing tests, missing coverage, low coverage, failing quality commands, and broken whole-app verification as reviewable findings that can become plans.",
5592
+ "",
5593
+ "## Work Item",
5594
+ "",
5595
+ `Title: ${workItem.title}`,
5596
+ `Work item ID: ${workItem.workItemId}`,
5597
+ `Project ID: ${workItem.projectId}`,
5598
+ `Test quality scan ID: ${workItem.testQualityScanId ?? "unknown"}`,
5599
+ "",
5600
+ "## Approved Project Brain Context",
5601
+ "",
5602
+ approvedContext || "No approved project-brain records were loaded. Inspect safe local metadata and summarize the context gap.",
5603
+ "",
5604
+ "## Requirements",
5605
+ "",
5606
+ "- Prefer the repository whole-app verification script when one exists, then focused package scripts.",
5607
+ "- Detect package managers from lockfiles and package metadata without uploading raw source.",
5608
+ "- Capture bounded summaries, exit codes, coverage metrics, and repository-relative safe paths only.",
5609
+ "- If coverage cannot be measured, return a missingCoverage finding instead of guessing.",
5610
+ "- If tests are missing or impossible to run, return a missingTests or testGap finding with a concrete plan and verification steps.",
5611
+ "- Redact secrets, absolute paths, env vars, process lists, tokens, provider sessions, and command lines containing secret-looking values.",
5612
+ "",
5613
+ "## Output Contract",
5614
+ "",
5615
+ "Print exactly one JSON object between the markers below. The CLI will submit only this structured test scan result back to Amistio.",
5616
+ "Accepted command kind values: verify, test, coverage, lint, typecheck, build, focused.",
5617
+ "Accepted command status values: passed, failed, skipped, missing, blocked.",
5618
+ "Accepted finding categories: missingTests, missingCoverage, lowCoverage, failingTests, failingQuality, missingCommand, flakyTests, unverifiedImplementation, testGap, other.",
5619
+ "",
5620
+ testQualityStart,
5621
+ '{"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":[]}',
5622
+ testQualityEnd,
5623
+ "",
5624
+ "Do not put Markdown fences around the markers. Do not implement fixes during this scan."
5625
+ ].join("\n");
5626
+ }
5627
+ function createImplementationTestGatePrompt(workItem) {
5628
+ return [
5629
+ "# Amistio Implementation Test Gate",
5630
+ "",
5631
+ "You are running locally through the Amistio CLI inside the user's repository.",
5632
+ "Run the required test gate before implementation completion, PR handoff, or runner-managed push.",
5633
+ "Do not modify files, install packages unless an already-approved preflight policy permits it, commit, push, or upload raw source.",
5634
+ "Run focused checks for the touched scope first when available, then the whole-app verification command when practical.",
5635
+ "If tests are missing or impossible, return blocked with a test-gap justification and a finding instead of marking the gate passed.",
5636
+ "",
5637
+ "## Work Item",
5638
+ "",
5639
+ `Title: ${workItem.title}`,
5640
+ `Work item ID: ${workItem.workItemId}`,
5641
+ `Project ID: ${workItem.projectId}`,
5642
+ `Implementation test gate ID: ${workItem.implementationTestGateId ?? "unknown"}`,
5643
+ `Source work item ID: ${workItem.sourceWorkItemId ?? "unknown"}`,
5644
+ "",
5645
+ "## Gate Request",
5646
+ "",
5647
+ workItem.sourceWish ?? "Run focused checks and whole-app verification before marking implementation complete.",
5648
+ "",
5649
+ "## Data Safety",
5650
+ "",
5651
+ "- Persist only bounded summaries, coverage metrics, exit codes, and repository-relative paths.",
5652
+ "- Do not include raw source, secrets, env vars, process lists, absolute local paths, credential values, tokens, provider sessions, or destructive shell output.",
5653
+ "- Use safePaths for repository-relative paths only; never include /absolute paths or ../ traversal.",
5654
+ "",
5655
+ "## Output Contract",
5656
+ "",
5657
+ "Print exactly one JSON object between the markers below. The CLI will submit only this structured gate result back to Amistio.",
5658
+ "Accepted outcome values: passed, failed, blocked, overridden.",
5659
+ "",
5660
+ implementationTestGateStart,
5661
+ '{"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":[]}',
5662
+ implementationTestGateEnd,
5663
+ "",
5664
+ "Do not put Markdown fences around the markers."
5665
+ ].join("\n");
5666
+ }
5667
+ function createWorkExecutionPrompt(workItem, context) {
5668
+ if (workItem.workKind === "brainGeneration") {
5669
+ return createBrainGenerationPrompt(workItem);
5670
+ }
5671
+ if (workItem.workKind === "planRevision") {
5672
+ return createPlanRevisionPrompt(workItem, context?.planRevision);
5673
+ }
5674
+ if (workItem.workKind === "assistantQuestion") {
5675
+ return createAssistantQuestionPrompt(workItem, context?.assistantQuestion);
5676
+ }
5677
+ if (workItem.workKind === "impactPreview") {
5678
+ return createImpactPreviewPrompt(workItem, context?.impactPreview);
5679
+ }
5680
+ if (workItem.workKind === "issueDiagnosis") {
5681
+ return createIssueDiagnosisPrompt(workItem, context?.issueDiagnosis);
5682
+ }
4808
5683
  if (workItem.workKind === "securityPostureScan") {
4809
5684
  return createSecurityPostureScanPrompt(workItem, context?.securityPostureScan);
4810
5685
  }
@@ -4817,6 +5692,12 @@ function createWorkExecutionPrompt(workItem, context) {
4817
5692
  if (workItem.workKind === "implementationVerification") {
4818
5693
  return createImplementationVerificationPrompt(workItem);
4819
5694
  }
5695
+ if (workItem.workKind === "testQualityScan") {
5696
+ return createTestQualityScanPrompt(workItem, context?.testQualityScan);
5697
+ }
5698
+ if (workItem.workKind === "implementationTestGate") {
5699
+ return createImplementationTestGatePrompt(workItem);
5700
+ }
4820
5701
  return [
4821
5702
  "# Amistio Work Execution",
4822
5703
  "",
@@ -4831,6 +5712,7 @@ function createWorkExecutionPrompt(workItem, context) {
4831
5712
  `Implementation scope: ${workItem.implementationScopeId ?? workItem.controllingAdrId ?? "work-item"}`,
4832
5713
  `Execution branch: ${workItem.executionBranch ?? "managed by Amistio CLI"}`,
4833
5714
  `Execution worktree key: ${workItem.executionWorktreeKey ?? "managed by Amistio CLI"}`,
5715
+ ...autopilotPromptLines(workItem),
4834
5716
  "",
4835
5717
  "## Rules",
4836
5718
  "",
@@ -4845,6 +5727,27 @@ function createWorkExecutionPrompt(workItem, context) {
4845
5727
  "- Run relevant verification commands when feasible and summarize results."
4846
5728
  ].join("\n");
4847
5729
  }
5730
+ function autopilotPromptLines(workItem) {
5731
+ const autopilot = autopilotWorkMetadata(workItem);
5732
+ if (!autopilot.autopilotAuthorizationId) {
5733
+ return [];
5734
+ }
5735
+ const lines = [
5736
+ `Autopilot authorization ID: ${autopilot.autopilotAuthorizationId}`,
5737
+ ...autopilot.autopilotCandidateId ? [`Autopilot candidate ID: ${autopilot.autopilotCandidateId}`] : [],
5738
+ ...autopilot.autopilotCandidateType ? [`Autopilot candidate type: ${autopilot.autopilotCandidateType}`] : [],
5739
+ `Autopilot outcome: ${autopilot.autopilotClassificationOutcome ?? "unknown"}`,
5740
+ `Autopilot policy: ${autopilot.autopilotPolicyVersion ?? "unknown"}`
5741
+ ];
5742
+ if (autopilot.autopilotAuthorization?.summary) {
5743
+ lines.push(`Autopilot summary: ${autopilot.autopilotAuthorization.summary}`);
5744
+ }
5745
+ 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.");
5746
+ return lines;
5747
+ }
5748
+ function autopilotWorkMetadata(workItem) {
5749
+ return workItem;
5750
+ }
4848
5751
  function createProjectContextRefreshPrompt(workItem, context) {
4849
5752
  const approvedContext = (context?.documents ?? []).filter((document) => document.status === "approved" || document.syncState === "approved" || document.syncState === "synced").slice(0, 24).map((document) => [
4850
5753
  `### ${document.title}`,
@@ -5016,6 +5919,8 @@ function createAppEvaluationScanPrompt(workItem, context) {
5016
5919
  "- Check app verification health: lint, typecheck, test, build, CI references, flaky or missing gates.",
5017
5920
  "- Check product and docs drift between approved context, plans, prompts, and current implementation.",
5018
5921
  "- Check old plans and prompts for cleanup candidates, but only propose lifecycle actions such as markCompleted, markSuperseded, archive, keepActive, or none.",
5922
+ "- 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.",
5923
+ "- 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
5924
  "- Check missing memory or workflow updates when repeated lessons or operational rules are visible.",
5020
5925
  "- Check release readiness, UX, accessibility, performance, reliability, and security-posture follow-through at a summary level.",
5021
5926
  "",
@@ -5028,6 +5933,10 @@ function createAppEvaluationScanPrompt(workItem, context) {
5028
5933
  "## Output Contract",
5029
5934
  "",
5030
5935
  "Print exactly one JSON object between the markers below. The CLI will submit only this structured scan result back to Amistio.",
5936
+ "Accepted category values: verification, productDrift, planCleanup, promptRot, missingMemory, releaseReadiness, ux, accessibility, performance, reliability, securityPosture, other.",
5937
+ "Accepted severity values: info, low, medium, high, critical.",
5938
+ "Accepted confidence values: low, medium, high.",
5939
+ "Accepted proposedLifecycleAction values: createPlan, updatePlan, markCompleted, markImplemented, markSuperseded, markBlocked, archive, keepActive, none.",
5031
5940
  "",
5032
5941
  appEvaluationStart,
5033
5942
  '{"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 +6267,8 @@ function parseAppEvaluationScanResult(output) {
5358
6267
  }
5359
6268
  const payload = output.slice(start + appEvaluationStart.length, end).trim();
5360
6269
  const parsed = JSON.parse(stripJsonFence(payload));
5361
- return appEvaluationScanResultSchema.parse(parsed);
6270
+ const normalized = normalizeAppEvaluationScanResultPaths(normalizeAppEvaluationScanResultEnums(parsed));
6271
+ return appEvaluationScanResultSchema.parse(normalized);
5362
6272
  }
5363
6273
  function parseProjectContextRefreshResult(output, options = {}) {
5364
6274
  const start = output.indexOf(projectContextRefreshStart);
@@ -5381,6 +6291,68 @@ function parseImplementationVerificationResult(output) {
5381
6291
  const parsed = JSON.parse(stripJsonFence(payload));
5382
6292
  return implementationVerificationResultSchema.parse(parsed);
5383
6293
  }
6294
+ function parseTestQualityScanResult(output) {
6295
+ const start = output.indexOf(testQualityStart);
6296
+ const end = output.indexOf(testQualityEnd, start + testQualityStart.length);
6297
+ if (start === -1 || end === -1 || end <= start) {
6298
+ throw new Error("Local AI scan did not return an Amistio test quality block.");
6299
+ }
6300
+ const payload = output.slice(start + testQualityStart.length, end).trim();
6301
+ const parsed = JSON.parse(stripJsonFence(payload));
6302
+ return testQualityScanResultSchema.parse(normalizeTestQualityScanResultCommandKinds(parsed));
6303
+ }
6304
+ function normalizeTestQualityScanResultCommandKinds(value) {
6305
+ if (!isObjectRecord(value)) {
6306
+ return value;
6307
+ }
6308
+ const normalized = { ...value };
6309
+ if (isObjectRecord(normalized.profile) && Array.isArray(normalized.profile.commands)) {
6310
+ normalized.profile = {
6311
+ ...normalized.profile,
6312
+ commands: normalized.profile.commands.map(normalizeTestCommandKindObject)
6313
+ };
6314
+ }
6315
+ if (Array.isArray(normalized.commandSummaries)) {
6316
+ normalized.commandSummaries = normalized.commandSummaries.map(normalizeTestCommandKindObject);
6317
+ }
6318
+ return normalized;
6319
+ }
6320
+ function normalizeTestCommandKindObject(value) {
6321
+ if (!isObjectRecord(value)) {
6322
+ return value;
6323
+ }
6324
+ return { ...value, kind: normalizeTestCommandKind(value.kind) };
6325
+ }
6326
+ function normalizeTestCommandKind(value) {
6327
+ if (typeof value !== "string") {
6328
+ return value;
6329
+ }
6330
+ const trimmed = value.trim();
6331
+ if (!trimmed) {
6332
+ return value;
6333
+ }
6334
+ const direct = canonicalTestCommandKinds.get(normalizeEnumKey(trimmed));
6335
+ if (direct) {
6336
+ return direct;
6337
+ }
6338
+ for (const segment of trimmed.split(/[\/,|]+/)) {
6339
+ const segmentKind = canonicalTestCommandKinds.get(normalizeEnumKey(segment));
6340
+ if (segmentKind) {
6341
+ return segmentKind;
6342
+ }
6343
+ }
6344
+ return "focused";
6345
+ }
6346
+ function parseImplementationTestGateResult(output) {
6347
+ const start = output.indexOf(implementationTestGateStart);
6348
+ const end = output.indexOf(implementationTestGateEnd, start + implementationTestGateStart.length);
6349
+ if (start === -1 || end === -1 || end <= start) {
6350
+ throw new Error("Local AI gate did not return an Amistio implementation test gate block.");
6351
+ }
6352
+ const payload = output.slice(start + implementationTestGateStart.length, end).trim();
6353
+ const parsed = JSON.parse(stripJsonFence(payload));
6354
+ return implementationTestGateResultSchema.parse(parsed);
6355
+ }
5384
6356
  function projectContextRefreshSubmissionFailureSummary(result) {
5385
6357
  if (result.refresh.status !== "failed" && result.workItem.status !== "failed") {
5386
6358
  return void 0;
@@ -5482,6 +6454,75 @@ function normalizeProjectContextRefreshEnums(value) {
5482
6454
  }
5483
6455
  return normalized;
5484
6456
  }
6457
+ function normalizeAppEvaluationScanResultEnums(value) {
6458
+ if (!isObjectRecord(value)) {
6459
+ return value;
6460
+ }
6461
+ const normalized = { ...value };
6462
+ if (Array.isArray(normalized.findings)) {
6463
+ normalized.findings = normalized.findings.map((finding) => {
6464
+ if (!isObjectRecord(finding)) {
6465
+ return finding;
6466
+ }
6467
+ const normalizedFinding = { ...finding };
6468
+ normalizedFinding.category = normalizeAppEvaluationFindingCategory(finding.category);
6469
+ return normalizedFinding;
6470
+ });
6471
+ }
6472
+ return normalized;
6473
+ }
6474
+ function normalizeAppEvaluationFindingCategory(value) {
6475
+ if (typeof value !== "string") {
6476
+ return value;
6477
+ }
6478
+ const trimmed = value.trim();
6479
+ if (!trimmed) {
6480
+ return "other";
6481
+ }
6482
+ const direct = canonicalAppEvaluationCategories.get(normalizeEnumKey(trimmed));
6483
+ if (direct) {
6484
+ return direct;
6485
+ }
6486
+ for (const segment of trimmed.split(/[\/,|]+/)) {
6487
+ const segmentCategory = canonicalAppEvaluationCategories.get(normalizeEnumKey(segment));
6488
+ if (segmentCategory) {
6489
+ return segmentCategory;
6490
+ }
6491
+ }
6492
+ return "other";
6493
+ }
6494
+ function normalizeEnumKey(value) {
6495
+ return value.trim().replace(/[^A-Za-z0-9]+/g, "").toLowerCase();
6496
+ }
6497
+ function normalizeAppEvaluationScanResultPaths(value) {
6498
+ if (!isObjectRecord(value)) {
6499
+ return value;
6500
+ }
6501
+ const normalized = { ...value };
6502
+ if (Array.isArray(normalized.findings)) {
6503
+ normalized.findings = normalized.findings.map((finding) => {
6504
+ if (!isObjectRecord(finding)) {
6505
+ return finding;
6506
+ }
6507
+ const normalizedFinding = { ...finding };
6508
+ if (Array.isArray(normalizedFinding.safePaths)) {
6509
+ normalizedFinding.safePaths = normalizedFinding.safePaths.map((safePath) => typeof safePath === "string" ? normalizeAppEvaluationRepoPath(safePath) : safePath);
6510
+ }
6511
+ if (typeof normalizedFinding.proposedPlanRepoPath === "string") {
6512
+ normalizedFinding.proposedPlanRepoPath = normalizeAppEvaluationRepoPath(normalizedFinding.proposedPlanRepoPath);
6513
+ }
6514
+ return normalizedFinding;
6515
+ });
6516
+ }
6517
+ return normalized;
6518
+ }
6519
+ function normalizeAppEvaluationRepoPath(value) {
6520
+ try {
6521
+ return normalizeRelativeProjectContextPath(value);
6522
+ } catch {
6523
+ throw new Error("App evaluation scan contains an unsafe repository path.");
6524
+ }
6525
+ }
5485
6526
  function normalizeProjectContextSliceKind(value) {
5486
6527
  if (typeof value !== "string") {
5487
6528
  return "unknown";
@@ -6188,6 +7229,7 @@ function buildBackgroundRunnerArgs(options) {
6188
7229
  if (options.maxIterations !== void 0) {
6189
7230
  args.push("--max-iterations", String(options.maxIterations));
6190
7231
  }
7232
+ args.push("--max-concurrent-work", String(options.maxConcurrentWork));
6191
7233
  args.push("--max-preflight-attempts", String(options.maxPreflightAttempts));
6192
7234
  args.push("--tool-timeout-seconds", String(options.toolTimeoutSeconds));
6193
7235
  if (!options.stream) {
@@ -6271,10 +7313,12 @@ function truncateProcessOutput(value) {
6271
7313
 
6272
7314
  // src/git-worktree.ts
6273
7315
  import { execFile as execFile5 } from "node:child_process";
6274
- import { mkdir as mkdir10, stat as stat5 } from "node:fs/promises";
7316
+ import { copyFile, lstat, mkdir as mkdir10, readdir as readdir6, stat as stat5 } from "node:fs/promises";
6275
7317
  import path14 from "node:path";
6276
7318
  import { promisify as promisify5 } from "node:util";
6277
7319
  var execFileAsync5 = promisify5(execFile5);
7320
+ var exactLocalEnvironmentFiles = /* @__PURE__ */ new Set([".env", ".env.local", ".env.development", ".env.development.local", ".env.test", ".env.test.local", ".env.production", ".env.production.local"]);
7321
+ var localEnvironmentFilePattern = /^\.env\.[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*\.local$/;
6278
7322
  function needsGitWorktreeIsolation(workItem) {
6279
7323
  return (workItem.workKind ?? "implementation") === "implementation";
6280
7324
  }
@@ -6299,7 +7343,8 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
6299
7343
  const worktreePath = localWorktreePath(repoRoot, identity.worktreeKey);
6300
7344
  if (await pathExists(worktreePath)) {
6301
7345
  await assertExistingWorktree(worktreePath, identity.branch);
6302
- return { ...identity, baseRevision, worktreePath };
7346
+ const preparedLocalEnvironmentFileCount2 = await prepareLocalWorktreeEnvironment(repoRoot, worktreePath);
7347
+ return { ...identity, baseRevision, worktreePath, ...preparedLocalEnvironmentFileCount2 ? { preparedLocalEnvironmentFileCount: preparedLocalEnvironmentFileCount2 } : {} };
6303
7348
  }
6304
7349
  await mkdir10(path14.dirname(worktreePath), { recursive: true });
6305
7350
  const branchExists = await gitCommandSucceeds(repoRoot, ["show-ref", "--verify", "--quiet", `refs/heads/${identity.branch}`]);
@@ -6307,7 +7352,8 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
6307
7352
  await gitOutput(repoRoot, worktreeArgs).catch((error) => {
6308
7353
  throw new Error(`Could not create Git worktree ${identity.worktreeKey} on ${identity.branch}: ${errorMessage2(error)}`);
6309
7354
  });
6310
- return { ...identity, baseRevision, worktreePath };
7355
+ const preparedLocalEnvironmentFileCount = await prepareLocalWorktreeEnvironment(repoRoot, worktreePath);
7356
+ return { ...identity, baseRevision, worktreePath, ...preparedLocalEnvironmentFileCount ? { preparedLocalEnvironmentFileCount } : {} };
6311
7357
  }
6312
7358
  function localWorktreePath(repoRoot, worktreeKey) {
6313
7359
  const repoName = path14.basename(repoRoot);
@@ -6334,6 +7380,55 @@ async function assertBaseRevision(repoRoot, baseRevision, currentHead) {
6334
7380
  throw new Error(`Work item base revision ${baseRevision} is not an ancestor of ${currentHead}; refresh the work item before implementation.`);
6335
7381
  }
6336
7382
  }
7383
+ async function prepareLocalWorktreeEnvironment(repoRoot, worktreePath) {
7384
+ const candidates = await localEnvironmentFileCandidates(repoRoot);
7385
+ let preparedCount = 0;
7386
+ for (const candidate of candidates) {
7387
+ const sourcePath = path14.join(repoRoot, candidate);
7388
+ const targetPath = path14.join(worktreePath, candidate);
7389
+ if (await pathExists(targetPath)) {
7390
+ continue;
7391
+ }
7392
+ if (await gitCommandSucceeds(repoRoot, ["ls-files", "--error-unmatch", "--", candidate])) {
7393
+ continue;
7394
+ }
7395
+ if (!await gitCommandSucceeds(worktreePath, ["check-ignore", "--quiet", "--", candidate])) {
7396
+ continue;
7397
+ }
7398
+ try {
7399
+ await copyFile(sourcePath, targetPath);
7400
+ preparedCount += 1;
7401
+ } catch (error) {
7402
+ throw new Error(`Could not prepare local worktree environment files: ${safeFileError(error)}`);
7403
+ }
7404
+ }
7405
+ return preparedCount;
7406
+ }
7407
+ async function localEnvironmentFileCandidates(repoRoot) {
7408
+ const names = new Set(exactLocalEnvironmentFiles);
7409
+ for (const entry of await readdir6(repoRoot)) {
7410
+ if (isAllowedLocalEnvironmentFile(entry)) {
7411
+ names.add(entry);
7412
+ }
7413
+ }
7414
+ const candidates = [];
7415
+ for (const name of [...names].sort()) {
7416
+ if (!isRootFileName(name)) {
7417
+ continue;
7418
+ }
7419
+ const source = await lstat(path14.join(repoRoot, name)).catch(() => void 0);
7420
+ if (source?.isFile()) {
7421
+ candidates.push(name);
7422
+ }
7423
+ }
7424
+ return candidates;
7425
+ }
7426
+ function isAllowedLocalEnvironmentFile(name) {
7427
+ return exactLocalEnvironmentFiles.has(name) || localEnvironmentFilePattern.test(name);
7428
+ }
7429
+ function isRootFileName(name) {
7430
+ return name === path14.basename(name) && !name.includes("/") && !name.includes("\\");
7431
+ }
6337
7432
  async function gitOutput(cwd, args) {
6338
7433
  const { stdout } = await execFileAsync5("git", args, { cwd, maxBuffer: 1024 * 1024 });
6339
7434
  return stdout.trim();
@@ -6356,6 +7451,12 @@ function slugify(value) {
6356
7451
  function errorMessage2(error) {
6357
7452
  return error instanceof Error ? error.message : String(error);
6358
7453
  }
7454
+ function safeFileError(error) {
7455
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") {
7456
+ return error.code;
7457
+ }
7458
+ return error instanceof Error ? error.name : "unknown file error";
7459
+ }
6359
7460
 
6360
7461
  // src/implementation-handoff.ts
6361
7462
  import { execFile as execFile6 } from "node:child_process";
@@ -6375,9 +7476,17 @@ async function completeImplementationHandoff(input) {
6375
7476
  headBranch
6376
7477
  };
6377
7478
  try {
7479
+ const artifactResult = await materializeApprovedArtifacts(input);
7480
+ if (artifactResult.blocked.length) {
7481
+ return blockedHandoff({
7482
+ ...common,
7483
+ artifacts: artifactResult,
7484
+ message: `Implementation handoff is blocked because ${artifactResult.blocked.length} approved artifact${artifactResult.blocked.length === 1 ? "" : "s"} could not be materialized safely.`
7485
+ });
7486
+ }
6378
7487
  const unmergedFiles = await gitOutput2(run, input.worktreePath, ["diff", "--name-only", "--diff-filter=U"]);
6379
7488
  if (unmergedFiles.trim()) {
6380
- return blockedHandoff({ ...common, message: "Implementation handoff is blocked because the worktree has unresolved merge conflicts." });
7489
+ return blockedHandoff({ ...common, artifacts: artifactResult, message: "Implementation handoff is blocked because the worktree has unresolved merge conflicts." });
6381
7490
  }
6382
7491
  const status = await gitOutput2(run, input.worktreePath, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
6383
7492
  if (!status) {
@@ -6385,6 +7494,7 @@ async function completeImplementationHandoff(input) {
6385
7494
  ...common,
6386
7495
  status: "noChanges",
6387
7496
  cleanupStatus: "notApplicable",
7497
+ artifacts: artifactResult,
6388
7498
  message: "Local execution completed with no repository changes to hand off."
6389
7499
  };
6390
7500
  }
@@ -6396,14 +7506,16 @@ async function completeImplementationHandoff(input) {
6396
7506
  ...common,
6397
7507
  provider,
6398
7508
  remoteName,
7509
+ artifacts: artifactResult,
6399
7510
  message: "Automated pull request handoff currently requires a GitHub remote. Commit and push manually, or link a GitHub repository."
6400
7511
  });
6401
7512
  }
6402
7513
  await gitOutput2(run, input.worktreePath, ["add", "-A"]);
6403
7514
  await gitOutput2(run, input.worktreePath, ["commit", "-m", commitSubject(input.workItem), "-m", commitBody(input)]);
7515
+ await rebaseBranchFromRemoteBase(run, input.worktreePath, { baseBranch, remoteName });
6404
7516
  const commitSha = await gitOutput2(run, input.worktreePath, ["rev-parse", "HEAD"]);
6405
7517
  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 } : {} });
7518
+ const pullRequest = await ensureGithubPullRequest(run, input.worktreePath, { artifacts: artifactResult, baseBranch, headBranch, workItem: input.workItem, ...input.verificationSummary ? { verificationSummary: input.verificationSummary } : {} });
6407
7519
  const cleanup = await cleanupWorktree(run, input);
6408
7520
  return {
6409
7521
  provider: "github",
@@ -6416,12 +7528,70 @@ async function completeImplementationHandoff(input) {
6416
7528
  prUrl: pullRequest.url,
6417
7529
  cleanupStatus: cleanup.status,
6418
7530
  ...cleanup.message ? { cleanupMessage: cleanup.message } : {},
7531
+ artifacts: artifactResult,
6419
7532
  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
7533
  };
6421
7534
  } catch (error) {
6422
7535
  return blockedHandoff({ ...common, message: "Implementation handoff is blocked and the local worktree was preserved for recovery.", error: safeErrorMessage(error) });
6423
7536
  }
6424
7537
  }
7538
+ async function materializeApprovedArtifacts(input) {
7539
+ const { selected: artifacts, skipped } = selectApprovedWorkArtifacts(input.workItem, input.approvedArtifacts ?? []);
7540
+ if (!artifacts.length) {
7541
+ return { included: [], skipped, blocked: [] };
7542
+ }
7543
+ let materialized;
7544
+ try {
7545
+ materialized = await materializeBrainDocuments(input.worktreePath, artifacts);
7546
+ } catch (error) {
7547
+ return { included: [], skipped, blocked: artifacts.map((artifact) => artifactStatus(artifact, "blocked", safeErrorMessage(error))) };
7548
+ }
7549
+ return {
7550
+ included: materialized.written.map((repoPath) => artifactStatus(findArtifactByPath(artifacts, repoPath), "included", "Approved artifact materialized into the implementation worktree.")),
7551
+ skipped: [...skipped, ...materialized.skipped.map((repoPath) => artifactStatus(findArtifactByPath(artifacts, repoPath), "skipped", "Approved artifact was already current in the implementation worktree."))],
7552
+ blocked: materialized.conflicts.map((conflict) => artifactStatus(findArtifactByPath(artifacts, conflictRepoPath(conflict)), "blocked", conflict))
7553
+ };
7554
+ }
7555
+ function selectApprovedWorkArtifacts(workItem, documents) {
7556
+ const draftId = workItem.generatedDraftId;
7557
+ const explicitDocumentIds = new Set([workItem.reviewDocumentId, workItem.impactDocumentId].filter((value) => Boolean(value)));
7558
+ const scopeIds = new Set([workItem.workItemId, workItem.controllingAdrId, workItem.implementationScopeId].filter((value) => Boolean(value)));
7559
+ const skipped = [];
7560
+ const selected = documents.filter((document) => {
7561
+ 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)));
7562
+ if (!inScope) return false;
7563
+ if (document.status !== "approved" && document.syncState !== "approved" && document.syncState !== "synced") {
7564
+ skipped.push(artifactStatus(document, "skipped", "Artifact is not approved for repository inclusion."));
7565
+ return false;
7566
+ }
7567
+ if (!["markdown", "html", void 0].includes(document.contentFormat)) {
7568
+ skipped.push(artifactStatus(document, "skipped", "Artifact format is not supported for implementation PR inclusion."));
7569
+ return false;
7570
+ }
7571
+ return true;
7572
+ });
7573
+ const byDocumentId = /* @__PURE__ */ new Map();
7574
+ for (const document of selected) {
7575
+ byDocumentId.set(document.documentId, document);
7576
+ }
7577
+ return { selected: [...byDocumentId.values()], skipped };
7578
+ }
7579
+ function findArtifactByPath(artifacts, repoPath) {
7580
+ return artifacts.find((artifact) => artifact.repoPath === repoPath) ?? artifacts[0];
7581
+ }
7582
+ function artifactStatus(artifact, status, message) {
7583
+ return {
7584
+ documentId: artifact.documentId,
7585
+ title: truncate(artifact.title, 200),
7586
+ repoPath: artifact.repoPath,
7587
+ contentFormat: artifact.contentFormat ?? "markdown",
7588
+ status,
7589
+ message: truncate(message, 600)
7590
+ };
7591
+ }
7592
+ function conflictRepoPath(conflict) {
7593
+ return conflict.split(":")[0]?.trim() ?? conflict;
7594
+ }
6425
7595
  async function ensureGithubPullRequest(run, cwd, input) {
6426
7596
  const existing = await run("gh", ["pr", "list", "--head", input.headBranch, "--base", input.baseBranch, "--state", "open", "--json", "number,url", "--limit", "1"], { cwd });
6427
7597
  const parsed = parsePullRequestList(existing.stdout);
@@ -6458,6 +7628,10 @@ async function resolveRemoteName(run, cwd) {
6458
7628
  }
6459
7629
  return remotes.includes("origin") ? "origin" : remotes[0];
6460
7630
  }
7631
+ async function rebaseBranchFromRemoteBase(run, cwd, input) {
7632
+ await gitOutput2(run, cwd, ["fetch", input.remoteName, input.baseBranch]);
7633
+ await gitOutput2(run, cwd, ["rebase", "FETCH_HEAD"]);
7634
+ }
6461
7635
  async function gitOutput2(run, cwd, args) {
6462
7636
  const result = await run("git", args, { cwd });
6463
7637
  return result.stdout.trim();
@@ -6513,9 +7687,23 @@ function pullRequestBody(input) {
6513
7687
  `Base branch: ${input.baseBranch}`,
6514
7688
  `Head branch: ${input.headBranch}`,
6515
7689
  "",
7690
+ ...artifactSummaryLines(input.artifacts),
7691
+ "",
6516
7692
  input.verificationSummary ?? "Verification summary was not reported by the local runner."
6517
7693
  ].join("\n");
6518
7694
  }
7695
+ function artifactSummaryLines(artifacts) {
7696
+ if (!artifacts || !artifacts.included.length && !artifacts.skipped.length && !artifacts.blocked.length) {
7697
+ return ["Approved artifacts: none materialized for this handoff."];
7698
+ }
7699
+ const lines = [
7700
+ `Approved artifacts: ${artifacts.included.length} included, ${artifacts.skipped.length} already current, ${artifacts.blocked.length} blocked.`
7701
+ ];
7702
+ for (const artifact of artifacts.included.slice(0, 12)) {
7703
+ lines.push(`- ${artifact.repoPath}${artifact.title ? ` (${artifact.title})` : ""}`);
7704
+ }
7705
+ return lines;
7706
+ }
6519
7707
  function truncate(value, maxLength) {
6520
7708
  return value.length <= maxLength ? value : value.slice(0, maxLength - 1).trimEnd();
6521
7709
  }
@@ -6556,7 +7744,8 @@ var DEFAULT_MAX_PREFLIGHT_ATTEMPTS = 3;
6556
7744
  var DEFAULT_TOOL_TIMEOUT_SECONDS = 30 * 60;
6557
7745
  var RUNNER_WORK_LEASE_SECONDS = 300;
6558
7746
  var RUNNER_WORK_LEASE_RENEWAL_MS = 12e4;
6559
- var runnerSupportedWorkKinds = ["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification"];
7747
+ var MAX_CONCURRENT_RUNNER_WORK = 4;
7748
+ var runnerSupportedWorkKinds = ["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification", "testQualityScan", "implementationTestGate"];
6560
7749
  program.name("amistio").description("Amistio project brain CLI").version(CLI_VERSION);
6561
7750
  program.command("init").description("Create Amistio control-plane folders for a new project").option("--root <path>", "Repository root", defaultRoot).action(async (options) => {
6562
7751
  const created = await initControlPlane(options.root);
@@ -6839,7 +8028,7 @@ work.command("list").description("List queued work without claiming it").option(
6839
8028
  return;
6840
8029
  }
6841
8030
  for (const item of workItems) {
6842
- console.log(`${item.workItemId} [${item.status}] ${item.title}`);
8031
+ console.log(`${item.workItemId} [${item.status}] ${item.title}${formatAutopilotListSuffix(item)}`);
6843
8032
  }
6844
8033
  });
6845
8034
  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 +8094,7 @@ program.command("orchestrate").description("Update the Amistio control plane thr
6905
8094
  process.exitCode = result.exitCode;
6906
8095
  }
6907
8096
  });
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) => {
8097
+ 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
8098
  const context = await loadPairedApiContext(options.root, options.apiUrl);
6910
8099
  if (!context) {
6911
8100
  console.log("Repository is not paired. Run `amistio pair` first.");
@@ -6923,6 +8112,11 @@ program.command("run").description("Claim and run approved Amistio work locally"
6923
8112
  machineId: runnerMachineId()
6924
8113
  });
6925
8114
  const resolvedOptions = { ...options, runnerId };
8115
+ if (resolvedOptions.maxConcurrentWork > MAX_CONCURRENT_RUNNER_WORK) {
8116
+ console.log(`--max-concurrent-work is capped at ${MAX_CONCURRENT_RUNNER_WORK}.`);
8117
+ process.exitCode = 1;
8118
+ return;
8119
+ }
6926
8120
  if (options.background) {
6927
8121
  if (options.dryRun) {
6928
8122
  console.log("Background runners cannot be started in dry-run mode.");
@@ -7077,7 +8271,7 @@ runner.command("stop").description("Stop a background runner for the paired repo
7077
8271
  console.log(stopResult === "stopped" ? `Stopped background runner ${record.runnerId}.` : `Marked background runner ${record.runnerId} stopped; process was not running.`);
7078
8272
  });
7079
8273
  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) => {
8274
+ 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
8275
  const context = await loadPairedApiContext(options.root, options.apiUrl);
7082
8276
  if (!context) {
7083
8277
  console.log("Repository is not paired. Run `amistio pair` first.");
@@ -7100,6 +8294,11 @@ runnerService.command("install").description("Install a user-level startup servi
7100
8294
  repositoryLinkId: context.metadata.repositoryLinkId,
7101
8295
  machineId: runnerMachineId()
7102
8296
  });
8297
+ if (options.maxConcurrentWork > MAX_CONCURRENT_RUNNER_WORK) {
8298
+ console.log(`--max-concurrent-work is capped at ${MAX_CONCURRENT_RUNNER_WORK}.`);
8299
+ process.exitCode = 1;
8300
+ return;
8301
+ }
7103
8302
  const args = buildBackgroundRunnerArgs({ ...options, runnerId, apiUrl: options.apiUrl, root: options.root });
7104
8303
  const serviceInput = {
7105
8304
  accountId: context.metadata.amistioAccountId,
@@ -7178,7 +8377,7 @@ async function runWatchIteration({ command, context, options, runnerId }) {
7178
8377
  });
7179
8378
  }
7180
8379
  if (!options.dryRun) {
7181
- const replayResult = await replayPendingBrainGenerationFinalizations({
8380
+ const replayResult = await replayPendingResultFinalizations({
7182
8381
  accountId: context.metadata.amistioAccountId,
7183
8382
  apiClient: context.client,
7184
8383
  projectId: context.metadata.amistioProjectId,
@@ -7189,7 +8388,8 @@ async function runWatchIteration({ command, context, options, runnerId }) {
7189
8388
  return replayResult;
7190
8389
  }
7191
8390
  }
7192
- return await runNextWorkItem({
8391
+ const activeClaimLaneIds = claimLaneIds(options.maxConcurrentWork);
8392
+ const runLane = (claimLaneId, laneIndex) => runNextWorkItem({
7193
8393
  apiClient: context.client,
7194
8394
  projectId: context.metadata.amistioProjectId,
7195
8395
  repositoryLinkId: context.metadata.repositoryLinkId,
@@ -7218,8 +8418,16 @@ async function runWatchIteration({ command, context, options, runnerId }) {
7218
8418
  suppressIdleOutput: Boolean(options.watch),
7219
8419
  maxPreflightAttempts: options.maxPreflightAttempts,
7220
8420
  toolTimeoutMs: options.toolTimeoutSeconds * 1e3,
7221
- verbose: Boolean(options.verbose)
8421
+ verbose: Boolean(options.verbose),
8422
+ claimLaneId,
8423
+ maxConcurrentWork: options.maxConcurrentWork,
8424
+ activeClaimLaneIds,
8425
+ skipRunnerCommands: laneIndex > 0
7222
8426
  });
8427
+ if (options.watch && !options.dryRun && options.maxConcurrentWork > 1) {
8428
+ return aggregateRunnerLaneResults(await Promise.all(activeClaimLaneIds.map((claimLaneId, laneIndex) => runLane(claimLaneId, laneIndex))));
8429
+ }
8430
+ return await runLane("default", 0);
7223
8431
  } catch (error) {
7224
8432
  if (!options.watch) {
7225
8433
  throw error;
@@ -7247,6 +8455,23 @@ ${detail}`);
7247
8455
  return { status: "failed", exitCode: 1, message };
7248
8456
  }
7249
8457
  }
8458
+ function claimLaneIds(maxConcurrentWork) {
8459
+ return Array.from({ length: maxConcurrentWork }, (_, index) => index === 0 ? "default" : `lane_${index + 1}`);
8460
+ }
8461
+ function supportsConcurrentLocalToolExecution(toolConfig) {
8462
+ return toolConfig.tool === "none" || toolConfig.effectiveInvocationChannel === "command" || toolConfig.effectiveTool === "custom";
8463
+ }
8464
+ function aggregateRunnerLaneResults(results) {
8465
+ const stopResult = results.find((result) => result.stopRunner);
8466
+ if (stopResult) return stopResult;
8467
+ const failedResult = results.find((result) => result.status === "failed");
8468
+ if (failedResult) return failedResult;
8469
+ const blockedResult = results.find((result) => result.status === "blocked");
8470
+ if (blockedResult) return blockedResult;
8471
+ const completedResult = results.find((result) => result.status === "completed" || result.status === "preview");
8472
+ if (completedResult) return completedResult;
8473
+ return results.find((result) => result.status === "idle") ?? { status: "idle", exitCode: 0 };
8474
+ }
7250
8475
  async function runAutoSyncCycle({ context, maxFileKb, quiet, quietDisabled, root, runnerId }) {
7251
8476
  const projectId = context.metadata.amistioProjectId;
7252
8477
  const repositoryLinkId = context.metadata.repositoryLinkId;
@@ -7326,7 +8551,11 @@ async function runNextWorkItem({
7326
8551
  commandContext,
7327
8552
  suppressIdleOutput,
7328
8553
  toolTimeoutMs,
7329
- verbose
8554
+ verbose,
8555
+ claimLaneId,
8556
+ maxConcurrentWork,
8557
+ activeClaimLaneIds,
8558
+ skipRunnerCommands
7330
8559
  }) {
7331
8560
  const toolConfig = await resolveRunnerToolConfig({
7332
8561
  apiClient,
@@ -7340,19 +8569,27 @@ async function runNextWorkItem({
7340
8569
  ...explicitTool ? { explicitTool } : {},
7341
8570
  ...toolCommand ? { toolCommand } : {}
7342
8571
  });
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);
8572
+ const effectiveMaxConcurrentWork = supportsConcurrentLocalToolExecution(toolConfig) ? maxConcurrentWork : 1;
8573
+ const effectiveActiveClaimLaneIds = claimLaneIds(effectiveMaxConcurrentWork);
8574
+ const heartbeatConcurrency = { maxConcurrentWork: effectiveMaxConcurrentWork, activeClaimLaneIds: effectiveActiveClaimLaneIds };
8575
+ if (claimLaneId !== "default" && effectiveMaxConcurrentWork === 1) {
8576
+ return { status: "idle", exitCode: 0 };
8577
+ }
8578
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, toolConfig.ready ? "online" : "blocked", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
8579
+ if (!skipRunnerCommands) {
8580
+ const commandResult = await runPendingRunnerCommand(apiClient, commandContext, runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
8581
+ if (commandResult.handled) {
8582
+ if (commandResult.message) {
8583
+ console.log(commandResult.message);
8584
+ }
8585
+ return { status: commandResult.succeeded ? "completed" : "failed", exitCode: commandResult.succeeded ? 0 : 1, ...commandResult.stopRunner ? { stopRunner: true } : {} };
7348
8586
  }
7349
- return { status: commandResult.succeeded ? "completed" : "failed", exitCode: commandResult.succeeded ? 0 : 1, ...commandResult.stopRunner ? { stopRunner: true } : {} };
7350
8587
  }
7351
8588
  if (!toolConfig.ready) {
7352
8589
  console.log(toolConfig.message);
7353
8590
  return { status: "blocked", exitCode: 1 };
7354
8591
  }
7355
- const result = await apiClient.claimWork(projectId, runnerId, repositoryLinkId, RUNNER_WORK_LEASE_SECONDS, runnerIsolationCapabilityMetadata());
8592
+ const result = await apiClient.claimWork(projectId, runnerId, repositoryLinkId, RUNNER_WORK_LEASE_SECONDS, runnerIsolationCapabilityMetadata({ claimLaneId, maxConcurrentWork: effectiveMaxConcurrentWork }));
7356
8593
  if (!result.workItem) {
7357
8594
  const nextAction = await loadProjectNextAction(apiClient, projectId, repositoryLinkId, root);
7358
8595
  const message = formatProjectNextAction(nextAction);
@@ -7370,17 +8607,17 @@ async function runNextWorkItem({
7370
8607
  });
7371
8608
  if (dryRun || toolConfig.tool === "none") {
7372
8609
  console.log(prompt);
7373
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
8610
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
7374
8611
  return { status: "preview", exitCode: 0 };
7375
8612
  }
7376
- const worktreeIsolation = await prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem: result.workItem });
8613
+ const worktreeIsolation = await prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem: result.workItem });
7377
8614
  if (worktreeIsolation.status !== "ready") {
7378
8615
  return { status: worktreeIsolation.status === "failed" ? "failed" : "blocked", exitCode: 1, message: worktreeIsolation.message };
7379
8616
  }
7380
8617
  const executionRoot = worktreeIsolation.isolation?.worktreePath ?? root;
7381
8618
  const isolationTelemetry = workItemIsolationTelemetry(result.workItem, worktreeIsolation.isolation);
7382
8619
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "running", {
7383
- ...runnerHeartbeatMetadata(toolConfig),
8620
+ ...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
7384
8621
  currentWorkItemId: result.workItem.workItemId,
7385
8622
  ...isolationTelemetry.implementationScopeId ? { currentImplementationScopeId: isolationTelemetry.implementationScopeId } : {},
7386
8623
  ...isolationTelemetry.executionWorktreeKey ? { currentWorktreeKey: isolationTelemetry.executionWorktreeKey } : {},
@@ -7403,6 +8640,10 @@ async function runNextWorkItem({
7403
8640
  isolationTelemetry
7404
8641
  });
7405
8642
  console.log(`Claimed ${result.workItem.workItemId}. Running ${preview.toolName}: ${preview.displayCommand}`);
8643
+ const autopilotClaimLine = formatAutopilotClaimLine(result.workItem);
8644
+ if (autopilotClaimLine) {
8645
+ console.log(autopilotClaimLine);
8646
+ }
7406
8647
  await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
7407
8648
  status: "running",
7408
8649
  summary: `Local runner started ${preview.toolName} execution.`,
@@ -7413,7 +8654,7 @@ async function runNextWorkItem({
7413
8654
  const providerSessionStore = new LocalToolSessionStore();
7414
8655
  const providerSessionId = sessionContext.toolSession ? await providerSessionStore.getProviderSessionId(sessionContext.toolSession.toolSessionId, preview.toolName) : void 0;
7415
8656
  let toolResult;
7416
- const stopLeaseRenewal = startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem: result.workItem, telemetry: isolationTelemetry });
8657
+ const stopLeaseRenewal = startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem: result.workItem, telemetry: isolationTelemetry, heartbeatConcurrency });
7417
8658
  try {
7418
8659
  toolResult = await runLocalTool({
7419
8660
  rootDir: executionRoot,
@@ -7439,7 +8680,7 @@ async function runNextWorkItem({
7439
8680
  const durationMs2 = Date.now() - startedAt;
7440
8681
  const message = `${preview.toolName} failed before returning a result.`;
7441
8682
  const settlements = await Promise.allSettled([
7442
- apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig)),
8683
+ apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency)),
7443
8684
  markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage3(error)),
7444
8685
  apiClient.updateWorkStatus(projectId, result.workItem.workItemId, "failed", `run_failed_${result.workItem.workItemId}_${result.workItem.attempt}_${runnerId}`, runnerId, {
7445
8686
  ...isolationTelemetry,
@@ -7622,6 +8863,42 @@ async function runNextWorkItem({
7622
8863
  return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
7623
8864
  }
7624
8865
  }
8866
+ if (result.workItem.workKind === "testQualityScan") {
8867
+ try {
8868
+ return await finalizeTestQualityScanWork({
8869
+ apiClient,
8870
+ durationMs: Date.now() - startedAt,
8871
+ projectId,
8872
+ repositoryLinkId,
8873
+ runnerId,
8874
+ sessionContext,
8875
+ toolConfig,
8876
+ toolName: preview.toolName,
8877
+ toolResult,
8878
+ workItem: result.workItem
8879
+ });
8880
+ } catch (error) {
8881
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
8882
+ }
8883
+ }
8884
+ if (result.workItem.workKind === "implementationTestGate") {
8885
+ try {
8886
+ return await finalizeImplementationTestGateWork({
8887
+ apiClient,
8888
+ durationMs: Date.now() - startedAt,
8889
+ projectId,
8890
+ repositoryLinkId,
8891
+ runnerId,
8892
+ sessionContext,
8893
+ toolConfig,
8894
+ toolName: preview.toolName,
8895
+ toolResult,
8896
+ workItem: result.workItem
8897
+ });
8898
+ } catch (error) {
8899
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
8900
+ }
8901
+ }
7625
8902
  let finalStatus = toolResult.exitCode === 0 ? "completed" : "failed";
7626
8903
  const durationMs = Date.now() - startedAt;
7627
8904
  const failureExcerpt = toolResult.exitCode === 0 ? void 0 : truncateLogExcerpt(toolResult.stderr || toolResult.stdout);
@@ -7636,14 +8913,27 @@ async function runNextWorkItem({
7636
8913
  metadata: { executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
7637
8914
  });
7638
8915
  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
8916
+ const approvedArtifacts = await apiClient.listBrainDocuments(projectId).then((response) => response.documents).catch((error) => {
8917
+ implementationHandoff = {
8918
+ status: "blocked",
8919
+ cleanupStatus: "pending",
8920
+ artifacts: { included: [], skipped: [], blocked: [] },
8921
+ message: "Implementation handoff is blocked because approved artifact metadata could not be loaded.",
8922
+ error: truncateLogExcerpt(errorDetail(error))
8923
+ };
8924
+ return void 0;
7646
8925
  });
8926
+ if (!implementationHandoff) {
8927
+ implementationHandoff = await completeImplementationHandoff({
8928
+ ...approvedArtifacts ? { approvedArtifacts } : {},
8929
+ primaryRepoRoot: root,
8930
+ ...repositoryLink ? { repositoryLink } : {},
8931
+ verificationSummary: "Local execution reported completion.",
8932
+ workItem: result.workItem,
8933
+ ...worktreeIsolation.isolation ? { worktreeIsolation: worktreeIsolation.isolation } : {},
8934
+ worktreePath: executionRoot
8935
+ });
8936
+ }
7647
8937
  finalStatus = implementationHandoff.status === "prReady" || implementationHandoff.status === "noChanges" ? "completed" : "blocked";
7648
8938
  finalMessage = implementationHandoff.message ?? "Implementation handoff finished.";
7649
8939
  finalError = implementationHandoff.error;
@@ -7661,31 +8951,48 @@ async function runNextWorkItem({
7661
8951
  ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
7662
8952
  ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
7663
8953
  });
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 } : {}
7687
- }
7688
- );
8954
+ let statusResult;
8955
+ try {
8956
+ statusResult = await apiClient.updateWorkStatus(
8957
+ projectId,
8958
+ result.workItem.workItemId,
8959
+ finalStatus,
8960
+ `run_${result.workItem.workItemId}_${randomUUID()}`,
8961
+ runnerId,
8962
+ {
8963
+ tool: preview.toolName,
8964
+ ...toolResult.model ? { model: toolResult.model } : {},
8965
+ durationMs,
8966
+ message: finalMessage,
8967
+ ...isolationTelemetry,
8968
+ ...finalStatus === "blocked" ? { blockerReason: finalMessage } : {},
8969
+ sessionPolicy: sessionContext.policy,
8970
+ sessionDecision: sessionContext.decision,
8971
+ sessionDecisionReason: sessionContext.reason,
8972
+ ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
8973
+ ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {},
8974
+ ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
8975
+ ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
8976
+ ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {},
8977
+ ...implementationHandoff ? { implementationHandoff } : {},
8978
+ ...finalError ? { error: finalError } : {}
8979
+ }
8980
+ );
8981
+ } catch (error) {
8982
+ if (error instanceof AmistioApiError && error.status === 409 && error.detail.includes("implementation_test_gate_required")) {
8983
+ const gateMessage = "Implementation test gate was queued and must pass before completion or PR handoff is finalized.";
8984
+ await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
8985
+ status: "queued",
8986
+ summary: gateMessage,
8987
+ idempotencyKey: `runner_milestone_test_gate_required_${result.workItem.workItemId}_${result.workItem.attempt}`,
8988
+ metadata: { tool: preview.toolName, durationMs, executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
8989
+ });
8990
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
8991
+ console.log(gateMessage);
8992
+ return { status: "blocked", exitCode: 0 };
8993
+ }
8994
+ throw error;
8995
+ }
7689
8996
  await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
7690
8997
  status: finalStatus,
7691
8998
  summary: finalMessage,
@@ -7699,19 +9006,27 @@ async function runNextWorkItem({
7699
9006
  executionBranch: isolationTelemetry.executionBranch ?? "",
7700
9007
  ...implementationHandoff?.status ? { handoffStatus: implementationHandoff.status } : {},
7701
9008
  ...implementationHandoff?.prUrl ? { prUrl: implementationHandoff.prUrl } : {},
7702
- ...implementationHandoff?.cleanupStatus ? { cleanupStatus: implementationHandoff.cleanupStatus } : {}
9009
+ ...implementationHandoff?.cleanupStatus ? { cleanupStatus: implementationHandoff.cleanupStatus } : {},
9010
+ ...implementationHandoff?.artifacts ? artifactHandoffMetadata(implementationHandoff.artifacts) : {}
7703
9011
  }
7704
9012
  });
7705
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
9013
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
7706
9014
  const durationSeconds = Math.round(durationMs / 1e3);
7707
9015
  console.log(`Marked ${statusResult.workItem.workItemId} ${statusResult.workItem.status} after ${durationSeconds}s.`);
7708
9016
  return { status: finalStatus, exitCode: finalStatus === "completed" ? toolResult.exitCode : 1 };
7709
9017
  }
9018
+ function artifactHandoffMetadata(artifacts) {
9019
+ return {
9020
+ artifactIncludedCount: artifacts.included.length,
9021
+ artifactSkippedCount: artifacts.skipped.length,
9022
+ artifactBlockedCount: artifacts.blocked.length
9023
+ };
9024
+ }
7710
9025
  async function loadWorkItemRepositoryLink(apiClient, projectId, repositoryLinkId) {
7711
9026
  const { repositoryLinks } = await apiClient.listRepositoryLinks(projectId);
7712
9027
  return repositoryLinks.find((link) => link.repositoryLinkId === repositoryLinkId && link.status !== "revoked");
7713
9028
  }
7714
- async function prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem }) {
9029
+ async function prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem }) {
7715
9030
  if (!needsGitWorktreeIsolation(workItem)) {
7716
9031
  return { status: "ready" };
7717
9032
  }
@@ -7726,9 +9041,9 @@ async function prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts,
7726
9041
  const isolation = await prepareGitWorktreeIsolation(root, workItem);
7727
9042
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
7728
9043
  status: "running",
7729
- summary: `Prepared Git worktree ${isolation.worktreeKey} on ${isolation.branch}.`,
9044
+ summary: isolation.preparedLocalEnvironmentFileCount ? `Prepared Git worktree ${isolation.worktreeKey} on ${isolation.branch} and local environment files.` : `Prepared Git worktree ${isolation.worktreeKey} on ${isolation.branch}.`,
7730
9045
  idempotencyKey: `runner_milestone_worktree_${workItem.workItemId}_${workItem.attempt}`,
7731
- metadata: { executionWorktreeKey: isolation.worktreeKey, executionBranch: isolation.branch, implementationScopeId: isolation.implementationScopeId }
9046
+ metadata: { executionWorktreeKey: isolation.worktreeKey, executionBranch: isolation.branch, implementationScopeId: isolation.implementationScopeId, ...isolation.preparedLocalEnvironmentFileCount ? { preparedLocalEnvironmentFileCount: isolation.preparedLocalEnvironmentFileCount } : {} }
7732
9047
  });
7733
9048
  return { status: "ready", isolation };
7734
9049
  } catch (error) {
@@ -7749,14 +9064,14 @@ async function prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts,
7749
9064
  metadata: { executionWorktreeKey: telemetry.executionWorktreeKey ?? "", executionBranch: telemetry.executionBranch ?? "", implementationScopeId: telemetry.implementationScopeId ?? "", attempt: workItem.attempt, maxAttempts: maxPreflightAttempts, error: message }
7750
9065
  });
7751
9066
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", {
7752
- ...runnerHeartbeatMetadata(toolConfig),
9067
+ ...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
7753
9068
  preferenceMessage: statusMessage
7754
9069
  });
7755
9070
  console.error(statusMessage);
7756
9071
  return { status: finalAttempt ? "failed" : "retrying", message: statusMessage };
7757
9072
  }
7758
9073
  }
7759
- function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem, telemetry }) {
9074
+ function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem, telemetry, heartbeatConcurrency }) {
7760
9075
  let stopped = false;
7761
9076
  const renew = async () => {
7762
9077
  if (stopped) return;
@@ -7767,7 +9082,7 @@ function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerI
7767
9082
  leaseExpiresAt
7768
9083
  });
7769
9084
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "running", {
7770
- ...runnerHeartbeatMetadata(toolConfig),
9085
+ ...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
7771
9086
  currentWorkItemId: workItem.workItemId,
7772
9087
  ...telemetry.implementationScopeId ? { currentImplementationScopeId: telemetry.implementationScopeId } : {},
7773
9088
  ...telemetry.executionWorktreeKey ? { currentWorktreeKey: telemetry.executionWorktreeKey } : {},
@@ -7783,6 +9098,7 @@ function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerI
7783
9098
  workItemId: workItem.workItemId,
7784
9099
  workTitle: workItem.title,
7785
9100
  ...workItem.workKind ? { workKind: workItem.workKind } : {},
9101
+ ...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
7786
9102
  message: "Runner could not renew the active work lease.",
7787
9103
  error: detail,
7788
9104
  machineId: runnerMachineId()
@@ -7822,6 +9138,7 @@ async function recordFinalizationFailure({ apiClient, durationMs, error, isolati
7822
9138
  workItemId: workItem.workItemId,
7823
9139
  workTitle: workItem.title,
7824
9140
  ...workItem.workKind ? { workKind: workItem.workKind } : {},
9141
+ ...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
7825
9142
  tool: toolName,
7826
9143
  durationMs,
7827
9144
  message,
@@ -7846,6 +9163,7 @@ function workItemIsolationTelemetry(workItem, isolation) {
7846
9163
  const repositoryLockId = isolation?.repositoryLockId ?? workItem.repositoryLockId;
7847
9164
  return {
7848
9165
  ...needsGitWorktreeIsolation(workItem) ? { isolationMode: "gitWorktree" } : workItem.isolationMode ? { isolationMode: workItem.isolationMode } : {},
9166
+ ...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
7849
9167
  ...workItem.claimLeaseId ? { claimLeaseId: workItem.claimLeaseId } : {},
7850
9168
  ...workItem.controllingAdrId ? { controllingAdrId: workItem.controllingAdrId } : {},
7851
9169
  ...implementationScopeId ? { implementationScopeId } : {},
@@ -7857,17 +9175,60 @@ function workItemIsolationTelemetry(workItem, isolation) {
7857
9175
  };
7858
9176
  }
7859
9177
  async function recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, input) {
9178
+ const autopilot = autopilotWorkMetadata2(workItem);
9179
+ const metadata = { ...autopilotRunnerMetadata(workItem), ...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {}, ...input.metadata ?? {} };
7860
9180
  await apiClient.recordActivityEvent(projectId, {
7861
9181
  eventType: "runnerMilestone",
7862
9182
  runnerId,
7863
9183
  repositoryLinkId,
9184
+ ...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
7864
9185
  relatedWorkItemId: workItem.workItemId,
7865
9186
  ...workItem.reviewDocumentId ? { relatedDocumentId: workItem.reviewDocumentId } : workItem.impactDocumentId ? { relatedDocumentId: workItem.impactDocumentId } : {},
7866
9187
  ...workItem.issueId ? { relatedIssueId: workItem.issueId } : {},
9188
+ ...autopilot.autopilotAuthorizationId ? { relatedAutopilotAuthorizationId: autopilot.autopilotAuthorizationId } : {},
9189
+ ...autopilot.autopilotCandidateId ? { relatedAutopilotCandidateId: autopilot.autopilotCandidateId } : {},
7867
9190
  ...workItem.generatedDraftId ? { generatedDraftId: workItem.generatedDraftId } : {},
7868
- ...input
9191
+ ...input,
9192
+ ...Object.keys(metadata).length ? { metadata } : {}
7869
9193
  }).catch(() => void 0);
7870
9194
  }
9195
+ function formatAutopilotListSuffix(workItem) {
9196
+ const autopilot = autopilotWorkMetadata2(workItem);
9197
+ if (!autopilot.autopilotAuthorizationId) {
9198
+ return "";
9199
+ }
9200
+ const details = [
9201
+ autopilot.autopilotClassificationOutcome ?? "authorized",
9202
+ workItem.workKind ?? "implementation",
9203
+ autopilot.autopilotCandidateType,
9204
+ autopilot.autopilotPolicyVersion ? `policy ${autopilot.autopilotPolicyVersion}` : void 0,
9205
+ `auth ${autopilot.autopilotAuthorizationId}`,
9206
+ autopilot.autopilotCandidateId ? `candidate ${autopilot.autopilotCandidateId}` : void 0
9207
+ ].filter(Boolean).join(", ");
9208
+ return ` (autopilot: ${details})`;
9209
+ }
9210
+ function formatAutopilotClaimLine(workItem) {
9211
+ const autopilot = autopilotWorkMetadata2(workItem);
9212
+ if (!autopilot.autopilotAuthorizationId) {
9213
+ return void 0;
9214
+ }
9215
+ const candidate = autopilot.autopilotCandidateId ? `, candidate ${autopilot.autopilotCandidateId}` : "";
9216
+ const candidateType = autopilot.autopilotCandidateType ? `, ${autopilot.autopilotCandidateType}` : "";
9217
+ return `Autopilot authorization: ${autopilot.autopilotAuthorizationId} (${autopilot.autopilotClassificationOutcome ?? "authorized"}, ${workItem.workKind ?? "implementation"}${candidateType}, policy ${autopilot.autopilotPolicyVersion ?? "unknown"}${candidate}). Local runner safety rules still apply.`;
9218
+ }
9219
+ function autopilotRunnerMetadata(workItem) {
9220
+ const autopilot = autopilotWorkMetadata2(workItem);
9221
+ return {
9222
+ ...autopilot.autopilotAuthorizationId ? { autopilotAuthorizationId: autopilot.autopilotAuthorizationId } : {},
9223
+ ...autopilot.autopilotCandidateId ? { autopilotCandidateId: autopilot.autopilotCandidateId } : {},
9224
+ ...autopilot.autopilotCandidateType ? { autopilotCandidateType: autopilot.autopilotCandidateType } : {},
9225
+ ...autopilot.autopilotClassificationOutcome ? { autopilotOutcome: autopilot.autopilotClassificationOutcome } : {},
9226
+ ...autopilot.autopilotPolicyVersion ? { autopilotPolicyVersion: autopilot.autopilotPolicyVersion } : {}
9227
+ };
9228
+ }
9229
+ function autopilotWorkMetadata2(workItem) {
9230
+ return workItem;
9231
+ }
7871
9232
  function logRejectedSettlements(action, settlements) {
7872
9233
  for (const settlement of settlements) {
7873
9234
  if (settlement.status === "rejected") {
@@ -7954,6 +9315,28 @@ async function replayPendingBrainGenerationFinalizations({ accountId, apiClient,
7954
9315
  console.log(message);
7955
9316
  return { status: "completed", exitCode: 0, message };
7956
9317
  }
9318
+ async function replayPendingResultFinalizations({ accountId, apiClient, projectId, repositoryLinkId, runnerId }) {
9319
+ const brainReplay = await replayPendingBrainGenerationFinalizations({ accountId, apiClient, projectId, repositoryLinkId, runnerId });
9320
+ if (brainReplay) {
9321
+ return brainReplay;
9322
+ }
9323
+ const pendingEntries = await listPendingDurableResultFinalizations({ accountId, projectId, repositoryLinkId, runnerId });
9324
+ if (!pendingEntries.length) {
9325
+ return void 0;
9326
+ }
9327
+ let completedCount = 0;
9328
+ for (const entry of pendingEntries) {
9329
+ const replay = await submitDurableResultFinalizationEntry(apiClient, entry, { recordReplayTelemetry: true });
9330
+ if (replay.status === "completed") {
9331
+ completedCount += 1;
9332
+ continue;
9333
+ }
9334
+ return { status: "failed", exitCode: 1, message: replay.message };
9335
+ }
9336
+ const message = `Replayed ${completedCount} pending runner result finalization${completedCount === 1 ? "" : "s"}.`;
9337
+ console.log(message);
9338
+ return { status: "completed", exitCode: 0, message };
9339
+ }
7957
9340
  async function submitBrainGenerationFinalizationEntry(apiClient, entry, options = {}) {
7958
9341
  let result;
7959
9342
  try {
@@ -7988,6 +9371,127 @@ async function submitBrainGenerationFinalizationEntry(apiClient, entry, options
7988
9371
  }
7989
9372
  return { status: "completed", workItem: result.workItem, documentCount: result.documents.length };
7990
9373
  }
9374
+ async function submitDurableResultFinalizationEntry(apiClient, entry, options = {}) {
9375
+ let response;
9376
+ try {
9377
+ response = await submitDurableResultMutation(apiClient, entry);
9378
+ } catch (error) {
9379
+ const detail = truncateLogExcerpt(errorMessage3(error));
9380
+ if (isRetryableApiError(error)) {
9381
+ const updated = await markDurableResultFinalizationRetry(entry, detail);
9382
+ const message2 = `Pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} could not be replayed yet (${updated.retryCount} attempt${updated.retryCount === 1 ? "" : "s"}): ${detail}`;
9383
+ console.error(message2);
9384
+ return { status: "failed", message: message2, retryable: true };
9385
+ }
9386
+ await markDurableResultFinalizationTerminal(entry, detail);
9387
+ const message = `Pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} reached a terminal API failure: ${detail}`;
9388
+ console.error(message);
9389
+ return { status: "failed", message, retryable: false };
9390
+ }
9391
+ const workItem = durableResultResponseWorkItem(response);
9392
+ await deleteDurableResultFinalizationEntry(entry).catch((error) => {
9393
+ console.error(`delete pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} failed: ${errorMessage3(error)}`);
9394
+ });
9395
+ if (options.recordReplayTelemetry) {
9396
+ await recordRunnerMilestone(apiClient, entry.projectId, workItem, entry.runnerId, entry.repositoryLinkId, {
9397
+ status: entry.result.status,
9398
+ summary: entry.result.status === "completed" ? `Replayed pending ${runnerResultFinalizationLabel(entry)} finalization.` : `Replayed pending ${runnerResultFinalizationLabel(entry)} failure finalization.`,
9399
+ idempotencyKey: `runner_milestone_result_replayed_${entry.workItemId}_${workItem.idempotencyKey}`,
9400
+ metadata: { replayedFinalization: true, resultKind: entry.resultKind, workKind: entry.workKind }
9401
+ });
9402
+ await apiClient.sendRunnerHeartbeat(entry.projectId, entry.runnerId, entry.repositoryLinkId, "online", {
9403
+ ...runnerHeartbeatMetadata(),
9404
+ preferenceMessage: "Pending result finalization replayed."
9405
+ }).catch(() => void 0);
9406
+ }
9407
+ return { status: "completed", workItem, response };
9408
+ }
9409
+ async function submitDurableResultMutation(apiClient, entry) {
9410
+ if (entry.resultKind === "assistantResult") {
9411
+ return apiClient.submitAssistantResult(entry.projectId, entry.workItemId, entry.result);
9412
+ }
9413
+ if (entry.resultKind === "impactPreviewResult") {
9414
+ return apiClient.submitImpactPreviewResult(entry.projectId, entry.workItemId, entry.result);
9415
+ }
9416
+ if (entry.resultKind === "issueDiagnosisResult") {
9417
+ return apiClient.submitIssueDiagnosisResult(entry.projectId, entry.workItemId, entry.result);
9418
+ }
9419
+ if (entry.resultKind === "securityPostureScanResult") {
9420
+ return apiClient.submitSecurityPostureScanResult(entry.projectId, entry.workItemId, entry.result);
9421
+ }
9422
+ if (entry.resultKind === "appEvaluationScanResult") {
9423
+ return apiClient.submitAppEvaluationScanResult(entry.projectId, entry.workItemId, entry.result);
9424
+ }
9425
+ if (entry.resultKind === "projectContextRefreshResult") {
9426
+ return apiClient.submitProjectContextRefreshResult(entry.projectId, entry.workItemId, entry.result);
9427
+ }
9428
+ if (entry.resultKind === "testQualityScanResult") {
9429
+ return apiClient.submitTestQualityScanResult(entry.projectId, entry.workItemId, entry.result);
9430
+ }
9431
+ if (entry.resultKind === "implementationTestGateResult") {
9432
+ return apiClient.submitImplementationTestGateResult(entry.projectId, entry.workItemId, entry.result);
9433
+ }
9434
+ return apiClient.submitImplementationVerificationResult(entry.projectId, entry.workItemId, entry.result);
9435
+ }
9436
+ function durableResultResponseWorkItem(response) {
9437
+ if (response && typeof response === "object" && "workItem" in response) {
9438
+ return response.workItem;
9439
+ }
9440
+ throw new Error("Runner result finalization response did not include a work item.");
9441
+ }
9442
+ function runnerResultFinalizationLabel(entry) {
9443
+ if (entry.workKind === "appEvaluationScan") return "app evaluation scan";
9444
+ if (entry.workKind === "securityPostureScan") return "security posture scan";
9445
+ if (entry.workKind === "projectContextRefresh") return "project context refresh";
9446
+ if (entry.workKind === "implementationVerification") return "implementation verification";
9447
+ if (entry.workKind === "testQualityScan") return "test quality scan";
9448
+ if (entry.workKind === "implementationTestGate") return "implementation test gate";
9449
+ if (entry.workKind === "issueDiagnosis") return "issue diagnosis";
9450
+ if (entry.workKind === "impactPreview") return "impact preview";
9451
+ return "assistant answer";
9452
+ }
9453
+ async function submitStagedDurableResultFinalization(apiClient, input) {
9454
+ const entry = createDurableResultFinalizationEntry(input);
9455
+ await upsertDurableResultFinalizationEntry(entry);
9456
+ return submitDurableResultFinalizationEntry(apiClient, entry);
9457
+ }
9458
+ async function submitPrimaryRunnerResult(apiClient, input) {
9459
+ const replay = await submitStagedDurableResultFinalization(apiClient, input);
9460
+ if (replay.status === "failed") {
9461
+ if (!replay.retryable) {
9462
+ throw new Error(replay.message);
9463
+ }
9464
+ return void 0;
9465
+ }
9466
+ return replay.response;
9467
+ }
9468
+ function pendingSessionTelemetry(sessionContext) {
9469
+ return {
9470
+ sessionPolicy: sessionContext.policy,
9471
+ sessionDecision: sessionContext.decision,
9472
+ sessionDecisionReason: sessionContext.reason,
9473
+ ...sessionContext.toolSession ? { toolSessionId: sessionContext.toolSession.toolSessionId } : {},
9474
+ ...sessionContext.toolSession?.sessionGroupKey ? { sessionGroupKey: sessionContext.toolSession.sessionGroupKey } : {}
9475
+ };
9476
+ }
9477
+ async function finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status, toolResult, workItemId }) {
9478
+ const settlements = await Promise.allSettled([
9479
+ finalizeToolSession({
9480
+ apiClient,
9481
+ projectId,
9482
+ status,
9483
+ runnerId,
9484
+ workItemId,
9485
+ stdout: toolResult.stdout,
9486
+ ...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
9487
+ ...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
9488
+ ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
9489
+ ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
9490
+ ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
9491
+ })
9492
+ ]);
9493
+ logRejectedSettlements("finalize runner result tool session", settlements);
9494
+ }
7991
9495
  async function finalizeBrainGenerationWork({
7992
9496
  apiClient,
7993
9497
  durationMs,
@@ -8145,28 +9649,9 @@ ${toolResult.stderr}`);
8145
9649
  answerError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8146
9650
  }
8147
9651
  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
- };
9652
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8168
9653
  if (answerResult) {
8169
- const result = await apiClient.submitAssistantResult(projectId, workItem.workItemId, {
9654
+ const resultMutation = {
8170
9655
  status: "completed",
8171
9656
  runnerId,
8172
9657
  idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID()}`,
@@ -8177,7 +9662,21 @@ ${toolResult.stderr}`);
8177
9662
  durationMs,
8178
9663
  ...sessionTelemetry,
8179
9664
  message: `${toolName} returned a project knowledge answer.`
9665
+ };
9666
+ const result = await submitPrimaryRunnerResult(apiClient, {
9667
+ accountId: workItem.accountId,
9668
+ projectId,
9669
+ repositoryLinkId,
9670
+ runnerId,
9671
+ workItemId: workItem.workItemId,
9672
+ workKind: "assistantQuestion",
9673
+ resultKind: "assistantResult",
9674
+ attempt: workItem.attempt,
9675
+ idempotencyKey: resultMutation.idempotencyKey,
9676
+ result: resultMutation
8180
9677
  });
9678
+ if (!result) return { status: "failed", exitCode: 1 };
9679
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8181
9680
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8182
9681
  status: "completed",
8183
9682
  summary: `${toolName} returned a project knowledge answer.`,
@@ -8188,7 +9687,7 @@ ${toolResult.stderr}`);
8188
9687
  console.log("Project knowledge answer returned.");
8189
9688
  return { status: "completed", exitCode: 0 };
8190
9689
  }
8191
- const failedResult = await apiClient.submitAssistantResult(projectId, workItem.workItemId, {
9690
+ const failedMutation = {
8192
9691
  status: "failed",
8193
9692
  runnerId,
8194
9693
  idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID()}`,
@@ -8197,7 +9696,21 @@ ${toolResult.stderr}`);
8197
9696
  ...sessionTelemetry,
8198
9697
  message: `${toolName} did not produce a valid project knowledge answer.`,
8199
9698
  ...answerError ? { error: answerError } : {}
9699
+ };
9700
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
9701
+ accountId: workItem.accountId,
9702
+ projectId,
9703
+ repositoryLinkId,
9704
+ runnerId,
9705
+ workItemId: workItem.workItemId,
9706
+ workKind: "assistantQuestion",
9707
+ resultKind: "assistantResult",
9708
+ attempt: workItem.attempt,
9709
+ idempotencyKey: failedMutation.idempotencyKey,
9710
+ result: failedMutation
8200
9711
  });
9712
+ if (!failedResult) return { status: "failed", exitCode: 1 };
9713
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8201
9714
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8202
9715
  status: "failed",
8203
9716
  summary: answerError ?? `${toolName} did not produce a valid project knowledge answer.`,
@@ -8234,29 +9747,10 @@ ${toolResult.stderr}`);
8234
9747
  previewError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8235
9748
  }
8236
9749
  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
- };
9750
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8257
9751
  if (report) {
8258
9752
  const metadata = await readProjectLink(root).catch(() => void 0);
8259
- const result = await apiClient.submitImpactPreviewResult(projectId, workItem.workItemId, {
9753
+ const resultMutation = {
8260
9754
  status: "completed",
8261
9755
  runnerId,
8262
9756
  idempotencyKey: `impact_${workItem.workItemId}_${randomUUID()}`,
@@ -8268,7 +9762,21 @@ ${toolResult.stderr}`);
8268
9762
  durationMs,
8269
9763
  ...sessionTelemetry,
8270
9764
  message: `${toolName} returned an implementation impact preview.`
9765
+ };
9766
+ const result = await submitPrimaryRunnerResult(apiClient, {
9767
+ accountId: workItem.accountId,
9768
+ projectId,
9769
+ repositoryLinkId,
9770
+ runnerId,
9771
+ workItemId: workItem.workItemId,
9772
+ workKind: "impactPreview",
9773
+ resultKind: "impactPreviewResult",
9774
+ attempt: workItem.attempt,
9775
+ idempotencyKey: resultMutation.idempotencyKey,
9776
+ result: resultMutation
8271
9777
  });
9778
+ if (!result) return { status: "failed", exitCode: 1 };
9779
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8272
9780
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8273
9781
  status: "completed",
8274
9782
  summary: `${toolName} returned an implementation impact preview.`,
@@ -8279,7 +9787,7 @@ ${toolResult.stderr}`);
8279
9787
  console.log(result.implementationWorkItem ? "Impact preview returned; implementation work is now queued." : "Impact preview returned.");
8280
9788
  return { status: "completed", exitCode: 0 };
8281
9789
  }
8282
- const failedResult = await apiClient.submitImpactPreviewResult(projectId, workItem.workItemId, {
9790
+ const failedMutation = {
8283
9791
  status: "failed",
8284
9792
  runnerId,
8285
9793
  idempotencyKey: `impact_${workItem.workItemId}_${randomUUID()}`,
@@ -8288,7 +9796,21 @@ ${toolResult.stderr}`);
8288
9796
  ...sessionTelemetry,
8289
9797
  message: `${toolName} did not produce a valid impact preview.`,
8290
9798
  ...previewError ? { error: previewError } : {}
9799
+ };
9800
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
9801
+ accountId: workItem.accountId,
9802
+ projectId,
9803
+ repositoryLinkId,
9804
+ runnerId,
9805
+ workItemId: workItem.workItemId,
9806
+ workKind: "impactPreview",
9807
+ resultKind: "impactPreviewResult",
9808
+ attempt: workItem.attempt,
9809
+ idempotencyKey: failedMutation.idempotencyKey,
9810
+ result: failedMutation
8291
9811
  });
9812
+ if (!failedResult) return { status: "failed", exitCode: 1 };
9813
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8292
9814
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8293
9815
  status: "failed",
8294
9816
  summary: previewError ?? `${toolName} did not produce a valid impact preview.`,
@@ -8324,28 +9846,9 @@ ${toolResult.stderr}`);
8324
9846
  diagnosisError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8325
9847
  }
8326
9848
  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
- };
9849
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8347
9850
  if (diagnosis) {
8348
- const result = await apiClient.submitIssueDiagnosisResult(projectId, workItem.workItemId, {
9851
+ const resultMutation = {
8349
9852
  status: "completed",
8350
9853
  runnerId,
8351
9854
  idempotencyKey: `issue_${workItem.workItemId}_${randomUUID()}`,
@@ -8354,7 +9857,21 @@ ${toolResult.stderr}`);
8354
9857
  durationMs,
8355
9858
  ...sessionTelemetry,
8356
9859
  message: `${toolName} returned an issue root-cause analysis.`
9860
+ };
9861
+ const result = await submitPrimaryRunnerResult(apiClient, {
9862
+ accountId: workItem.accountId,
9863
+ projectId,
9864
+ repositoryLinkId,
9865
+ runnerId,
9866
+ workItemId: workItem.workItemId,
9867
+ workKind: "issueDiagnosis",
9868
+ resultKind: "issueDiagnosisResult",
9869
+ attempt: workItem.attempt,
9870
+ idempotencyKey: resultMutation.idempotencyKey,
9871
+ result: resultMutation
8357
9872
  });
9873
+ if (!result) return { status: "failed", exitCode: 1 };
9874
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8358
9875
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8359
9876
  status: "completed",
8360
9877
  summary: `${toolName} returned an issue root-cause analysis.`,
@@ -8365,7 +9882,7 @@ ${toolResult.stderr}`);
8365
9882
  console.log("Issue diagnosis returned for approval.");
8366
9883
  return { status: "completed", exitCode: 0 };
8367
9884
  }
8368
- const failedResult = await apiClient.submitIssueDiagnosisResult(projectId, workItem.workItemId, {
9885
+ const failedMutation = {
8369
9886
  status: "failed",
8370
9887
  runnerId,
8371
9888
  idempotencyKey: `issue_${workItem.workItemId}_${randomUUID()}`,
@@ -8374,7 +9891,21 @@ ${toolResult.stderr}`);
8374
9891
  ...sessionTelemetry,
8375
9892
  message: `${toolName} did not produce a valid issue diagnosis.`,
8376
9893
  ...diagnosisError ? { error: diagnosisError } : {}
9894
+ };
9895
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
9896
+ accountId: workItem.accountId,
9897
+ projectId,
9898
+ repositoryLinkId,
9899
+ runnerId,
9900
+ workItemId: workItem.workItemId,
9901
+ workKind: "issueDiagnosis",
9902
+ resultKind: "issueDiagnosisResult",
9903
+ attempt: workItem.attempt,
9904
+ idempotencyKey: failedMutation.idempotencyKey,
9905
+ result: failedMutation
8377
9906
  });
9907
+ if (!failedResult) return { status: "failed", exitCode: 1 };
9908
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8378
9909
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8379
9910
  status: "failed",
8380
9911
  summary: diagnosisError ?? `${toolName} did not produce a valid issue diagnosis.`,
@@ -8410,28 +9941,9 @@ ${toolResult.stderr}`);
8410
9941
  scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8411
9942
  }
8412
9943
  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
- };
9944
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8433
9945
  if (scanResult) {
8434
- const result = await apiClient.submitSecurityPostureScanResult(projectId, workItem.workItemId, {
9946
+ const resultMutation = {
8435
9947
  status: "completed",
8436
9948
  runnerId,
8437
9949
  idempotencyKey: `security_${workItem.workItemId}_${randomUUID()}`,
@@ -8440,7 +9952,21 @@ ${toolResult.stderr}`);
8440
9952
  durationMs,
8441
9953
  ...sessionTelemetry,
8442
9954
  message: `${toolName} returned a security posture scan.`
9955
+ };
9956
+ const result = await submitPrimaryRunnerResult(apiClient, {
9957
+ accountId: workItem.accountId,
9958
+ projectId,
9959
+ repositoryLinkId,
9960
+ runnerId,
9961
+ workItemId: workItem.workItemId,
9962
+ workKind: "securityPostureScan",
9963
+ resultKind: "securityPostureScanResult",
9964
+ attempt: workItem.attempt,
9965
+ idempotencyKey: resultMutation.idempotencyKey,
9966
+ result: resultMutation
8443
9967
  });
9968
+ if (!result) return { status: "failed", exitCode: 1 };
9969
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8444
9970
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8445
9971
  status: "completed",
8446
9972
  summary: `${toolName} returned a security posture scan.`,
@@ -8451,7 +9977,7 @@ ${toolResult.stderr}`);
8451
9977
  console.log("Security posture scan returned for review.");
8452
9978
  return { status: "completed", exitCode: 0 };
8453
9979
  }
8454
- const failedResult = await apiClient.submitSecurityPostureScanResult(projectId, workItem.workItemId, {
9980
+ const failedMutation = {
8455
9981
  status: "failed",
8456
9982
  runnerId,
8457
9983
  idempotencyKey: `security_${workItem.workItemId}_${randomUUID()}`,
@@ -8460,7 +9986,21 @@ ${toolResult.stderr}`);
8460
9986
  ...sessionTelemetry,
8461
9987
  message: `${toolName} did not produce a valid security posture scan.`,
8462
9988
  ...scanError ? { error: scanError } : {}
9989
+ };
9990
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
9991
+ accountId: workItem.accountId,
9992
+ projectId,
9993
+ repositoryLinkId,
9994
+ runnerId,
9995
+ workItemId: workItem.workItemId,
9996
+ workKind: "securityPostureScan",
9997
+ resultKind: "securityPostureScanResult",
9998
+ attempt: workItem.attempt,
9999
+ idempotencyKey: failedMutation.idempotencyKey,
10000
+ result: failedMutation
8463
10001
  });
10002
+ if (!failedResult) return { status: "failed", exitCode: 1 };
10003
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8464
10004
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8465
10005
  status: "failed",
8466
10006
  summary: scanError ?? `${toolName} did not produce a valid security posture scan.`,
@@ -8496,28 +10036,9 @@ ${toolResult.stderr}`);
8496
10036
  scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8497
10037
  }
8498
10038
  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
- };
10039
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8519
10040
  if (scanResult) {
8520
- const result = await apiClient.submitAppEvaluationScanResult(projectId, workItem.workItemId, {
10041
+ const resultMutation = {
8521
10042
  status: "completed",
8522
10043
  runnerId,
8523
10044
  idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID()}`,
@@ -8526,7 +10047,21 @@ ${toolResult.stderr}`);
8526
10047
  durationMs,
8527
10048
  ...sessionTelemetry,
8528
10049
  message: `${toolName} returned an app evaluation scan.`
10050
+ };
10051
+ const result = await submitPrimaryRunnerResult(apiClient, {
10052
+ accountId: workItem.accountId,
10053
+ projectId,
10054
+ repositoryLinkId,
10055
+ runnerId,
10056
+ workItemId: workItem.workItemId,
10057
+ workKind: "appEvaluationScan",
10058
+ resultKind: "appEvaluationScanResult",
10059
+ attempt: workItem.attempt,
10060
+ idempotencyKey: resultMutation.idempotencyKey,
10061
+ result: resultMutation
8529
10062
  });
10063
+ if (!result) return { status: "failed", exitCode: 1 };
10064
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8530
10065
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8531
10066
  status: "completed",
8532
10067
  summary: `${toolName} returned an app evaluation scan.`,
@@ -8537,7 +10072,7 @@ ${toolResult.stderr}`);
8537
10072
  console.log("App evaluation scan returned for review.");
8538
10073
  return { status: "completed", exitCode: 0 };
8539
10074
  }
8540
- const failedResult = await apiClient.submitAppEvaluationScanResult(projectId, workItem.workItemId, {
10075
+ const failedMutation = {
8541
10076
  status: "failed",
8542
10077
  runnerId,
8543
10078
  idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID()}`,
@@ -8546,7 +10081,21 @@ ${toolResult.stderr}`);
8546
10081
  ...sessionTelemetry,
8547
10082
  message: `${toolName} did not produce a valid app evaluation scan.`,
8548
10083
  ...scanError ? { error: scanError } : {}
10084
+ };
10085
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
10086
+ accountId: workItem.accountId,
10087
+ projectId,
10088
+ repositoryLinkId,
10089
+ runnerId,
10090
+ workItemId: workItem.workItemId,
10091
+ workKind: "appEvaluationScan",
10092
+ resultKind: "appEvaluationScanResult",
10093
+ attempt: workItem.attempt,
10094
+ idempotencyKey: failedMutation.idempotencyKey,
10095
+ result: failedMutation
8549
10096
  });
10097
+ if (!failedResult) return { status: "failed", exitCode: 1 };
10098
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8550
10099
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8551
10100
  status: "failed",
8552
10101
  summary: scanError ?? `${toolName} did not produce a valid app evaluation scan.`,
@@ -8583,28 +10132,9 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
8583
10132
  refreshError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8584
10133
  }
8585
10134
  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
- };
10135
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8606
10136
  if (refreshResult) {
8607
- const result = await apiClient.submitProjectContextRefreshResult(projectId, workItem.workItemId, {
10137
+ const resultMutation = {
8608
10138
  status: "completed",
8609
10139
  runnerId,
8610
10140
  idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID()}`,
@@ -8613,7 +10143,21 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
8613
10143
  durationMs,
8614
10144
  ...sessionTelemetry,
8615
10145
  message: `${toolName} returned a project context refresh.`
10146
+ };
10147
+ const result = await submitPrimaryRunnerResult(apiClient, {
10148
+ accountId: workItem.accountId,
10149
+ projectId,
10150
+ repositoryLinkId,
10151
+ runnerId,
10152
+ workItemId: workItem.workItemId,
10153
+ workKind: "projectContextRefresh",
10154
+ resultKind: "projectContextRefreshResult",
10155
+ attempt: workItem.attempt,
10156
+ idempotencyKey: resultMutation.idempotencyKey,
10157
+ result: resultMutation
8616
10158
  });
10159
+ if (!result) return { status: "failed", exitCode: 1 };
10160
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8617
10161
  const failureSummary = projectContextRefreshSubmissionFailureSummary(result);
8618
10162
  if (failureSummary) {
8619
10163
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
@@ -8636,7 +10180,7 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
8636
10180
  console.log("Project context refresh returned for review.");
8637
10181
  return { status: "completed", exitCode: 0 };
8638
10182
  }
8639
- const failedResult = await apiClient.submitProjectContextRefreshResult(projectId, workItem.workItemId, {
10183
+ const failedMutation = {
8640
10184
  status: "failed",
8641
10185
  runnerId,
8642
10186
  idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID()}`,
@@ -8645,7 +10189,21 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
8645
10189
  ...sessionTelemetry,
8646
10190
  message: `${toolName} did not produce a valid project context refresh.`,
8647
10191
  ...refreshError ? { error: refreshError } : {}
10192
+ };
10193
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
10194
+ accountId: workItem.accountId,
10195
+ projectId,
10196
+ repositoryLinkId,
10197
+ runnerId,
10198
+ workItemId: workItem.workItemId,
10199
+ workKind: "projectContextRefresh",
10200
+ resultKind: "projectContextRefreshResult",
10201
+ attempt: workItem.attempt,
10202
+ idempotencyKey: failedMutation.idempotencyKey,
10203
+ result: failedMutation
8648
10204
  });
10205
+ if (!failedResult) return { status: "failed", exitCode: 1 };
10206
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8649
10207
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8650
10208
  status: "failed",
8651
10209
  summary: refreshError ?? `${toolName} did not produce a valid project context refresh.`,
@@ -8681,28 +10239,9 @@ ${toolResult.stderr}`);
8681
10239
  verificationError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
8682
10240
  }
8683
10241
  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
- };
10242
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
8704
10243
  if (verificationResult) {
8705
- const result = await apiClient.submitImplementationVerificationResult(projectId, workItem.workItemId, {
10244
+ const resultMutation = {
8706
10245
  status: "completed",
8707
10246
  runnerId,
8708
10247
  idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID()}`,
@@ -8711,7 +10250,21 @@ ${toolResult.stderr}`);
8711
10250
  durationMs,
8712
10251
  ...sessionTelemetry,
8713
10252
  message: `${toolName} returned implementation verification proof.`
10253
+ };
10254
+ const result = await submitPrimaryRunnerResult(apiClient, {
10255
+ accountId: workItem.accountId,
10256
+ projectId,
10257
+ repositoryLinkId,
10258
+ runnerId,
10259
+ workItemId: workItem.workItemId,
10260
+ workKind: "implementationVerification",
10261
+ resultKind: "implementationVerificationResult",
10262
+ attempt: workItem.attempt,
10263
+ idempotencyKey: resultMutation.idempotencyKey,
10264
+ result: resultMutation
8714
10265
  });
10266
+ if (!result) return { status: "failed", exitCode: 1 };
10267
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8715
10268
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8716
10269
  status: verificationResult.outcome === "verifiedImplemented" ? "completed" : verificationResult.outcome === "verificationBlocked" ? "blocked" : "warning",
8717
10270
  summary: verificationResult.summary,
@@ -8722,7 +10275,7 @@ ${toolResult.stderr}`);
8722
10275
  console.log("Implementation verification returned for review.");
8723
10276
  return { status: "completed", exitCode: 0 };
8724
10277
  }
8725
- const failedResult = await apiClient.submitImplementationVerificationResult(projectId, workItem.workItemId, {
10278
+ const failedMutation = {
8726
10279
  status: "failed",
8727
10280
  runnerId,
8728
10281
  idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID()}`,
@@ -8731,7 +10284,21 @@ ${toolResult.stderr}`);
8731
10284
  ...sessionTelemetry,
8732
10285
  message: `${toolName} did not produce valid implementation verification proof.`,
8733
10286
  ...verificationError ? { error: verificationError } : {}
10287
+ };
10288
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
10289
+ accountId: workItem.accountId,
10290
+ projectId,
10291
+ repositoryLinkId,
10292
+ runnerId,
10293
+ workItemId: workItem.workItemId,
10294
+ workKind: "implementationVerification",
10295
+ resultKind: "implementationVerificationResult",
10296
+ attempt: workItem.attempt,
10297
+ idempotencyKey: failedMutation.idempotencyKey,
10298
+ result: failedMutation
8734
10299
  });
10300
+ if (!failedResult) return { status: "failed", exitCode: 1 };
10301
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
8735
10302
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
8736
10303
  status: "failed",
8737
10304
  summary: verificationError ?? `${toolName} did not produce valid implementation verification proof.`,
@@ -8742,6 +10309,196 @@ ${toolResult.stderr}`);
8742
10309
  console.error(verificationError ?? "Local runner implementation verification failed.");
8743
10310
  return { status: "failed", exitCode: toolResult.exitCode || 1 };
8744
10311
  }
10312
+ async function finalizeTestQualityScanWork({
10313
+ apiClient,
10314
+ durationMs,
10315
+ projectId,
10316
+ repositoryLinkId,
10317
+ runnerId,
10318
+ sessionContext,
10319
+ toolConfig,
10320
+ toolName,
10321
+ toolResult,
10322
+ workItem
10323
+ }) {
10324
+ let scanResult = void 0;
10325
+ let scanError;
10326
+ if (toolResult.exitCode === 0) {
10327
+ try {
10328
+ scanResult = parseTestQualityScanResult(`${toolResult.stdout}
10329
+ ${toolResult.stderr}`);
10330
+ } catch (error) {
10331
+ scanError = errorMessage3(error);
10332
+ }
10333
+ } else {
10334
+ scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
10335
+ }
10336
+ const finalStatus = scanResult ? "completed" : "failed";
10337
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
10338
+ if (scanResult) {
10339
+ const resultMutation = {
10340
+ status: "completed",
10341
+ runnerId,
10342
+ idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID()}`,
10343
+ result: scanResult,
10344
+ tool: toolName,
10345
+ durationMs,
10346
+ ...sessionTelemetry,
10347
+ message: `${toolName} returned a test quality scan.`
10348
+ };
10349
+ const result = await submitPrimaryRunnerResult(apiClient, {
10350
+ accountId: workItem.accountId,
10351
+ projectId,
10352
+ repositoryLinkId,
10353
+ runnerId,
10354
+ workItemId: workItem.workItemId,
10355
+ workKind: "testQualityScan",
10356
+ resultKind: "testQualityScanResult",
10357
+ attempt: workItem.attempt,
10358
+ idempotencyKey: resultMutation.idempotencyKey,
10359
+ result: resultMutation
10360
+ });
10361
+ if (!result) return { status: "failed", exitCode: 1 };
10362
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10363
+ await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10364
+ status: scanResult.findings.some((finding) => finding.severity === "critical" || finding.severity === "high") ? "warning" : "completed",
10365
+ summary: scanResult.summary,
10366
+ idempotencyKey: `runner_milestone_test_quality_completed_${workItem.workItemId}_${result.workItem.idempotencyKey}`,
10367
+ metadata: { tool: toolName, durationMs, findingCount: scanResult.findings.length, commandCount: scanResult.commandSummaries.length, verificationSummary: scanResult.verificationPlan.join(" | ") }
10368
+ });
10369
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
10370
+ console.log("Test quality scan returned for review.");
10371
+ return { status: "completed", exitCode: 0 };
10372
+ }
10373
+ const failedMutation = {
10374
+ status: "failed",
10375
+ runnerId,
10376
+ idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID()}`,
10377
+ tool: toolName,
10378
+ durationMs,
10379
+ ...sessionTelemetry,
10380
+ message: `${toolName} did not produce a valid test quality scan.`,
10381
+ ...scanError ? { error: scanError } : {}
10382
+ };
10383
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
10384
+ accountId: workItem.accountId,
10385
+ projectId,
10386
+ repositoryLinkId,
10387
+ runnerId,
10388
+ workItemId: workItem.workItemId,
10389
+ workKind: "testQualityScan",
10390
+ resultKind: "testQualityScanResult",
10391
+ attempt: workItem.attempt,
10392
+ idempotencyKey: failedMutation.idempotencyKey,
10393
+ result: failedMutation
10394
+ });
10395
+ if (!failedResult) return { status: "failed", exitCode: 1 };
10396
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10397
+ await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10398
+ status: "failed",
10399
+ summary: scanError ?? `${toolName} did not produce a valid test quality scan.`,
10400
+ idempotencyKey: `runner_milestone_test_quality_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
10401
+ metadata: { tool: toolName, durationMs, verificationSummary: "Test quality output did not include valid structured JSON." }
10402
+ });
10403
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
10404
+ console.error(scanError ?? "Local runner test quality scan failed.");
10405
+ return { status: "failed", exitCode: toolResult.exitCode || 1 };
10406
+ }
10407
+ async function finalizeImplementationTestGateWork({
10408
+ apiClient,
10409
+ durationMs,
10410
+ projectId,
10411
+ repositoryLinkId,
10412
+ runnerId,
10413
+ sessionContext,
10414
+ toolConfig,
10415
+ toolName,
10416
+ toolResult,
10417
+ workItem
10418
+ }) {
10419
+ let gateResult = void 0;
10420
+ let gateError;
10421
+ if (toolResult.exitCode === 0) {
10422
+ try {
10423
+ gateResult = parseImplementationTestGateResult(`${toolResult.stdout}
10424
+ ${toolResult.stderr}`);
10425
+ } catch (error) {
10426
+ gateError = errorMessage3(error);
10427
+ }
10428
+ } else {
10429
+ gateError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
10430
+ }
10431
+ const finalStatus = gateResult ? "completed" : "failed";
10432
+ const sessionTelemetry = pendingSessionTelemetry(sessionContext);
10433
+ if (gateResult) {
10434
+ const resultMutation = {
10435
+ status: "completed",
10436
+ runnerId,
10437
+ idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID()}`,
10438
+ result: gateResult,
10439
+ tool: toolName,
10440
+ durationMs,
10441
+ ...sessionTelemetry,
10442
+ message: `${toolName} returned an implementation test gate result.`
10443
+ };
10444
+ const result = await submitPrimaryRunnerResult(apiClient, {
10445
+ accountId: workItem.accountId,
10446
+ projectId,
10447
+ repositoryLinkId,
10448
+ runnerId,
10449
+ workItemId: workItem.workItemId,
10450
+ workKind: "implementationTestGate",
10451
+ resultKind: "implementationTestGateResult",
10452
+ attempt: workItem.attempt,
10453
+ idempotencyKey: resultMutation.idempotencyKey,
10454
+ result: resultMutation
10455
+ });
10456
+ if (!result) return { status: "failed", exitCode: 1 };
10457
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10458
+ await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10459
+ status: gateResult.outcome === "passed" ? "completed" : gateResult.outcome === "blocked" ? "blocked" : "failed",
10460
+ summary: gateResult.summary,
10461
+ idempotencyKey: `runner_milestone_implementation_test_gate_completed_${workItem.workItemId}_${result.workItem.idempotencyKey}`,
10462
+ metadata: { tool: toolName, durationMs, outcome: gateResult.outcome, findingCount: gateResult.findings.length, commandCount: gateResult.commandSummaries.length, verificationSummary: gateResult.verificationPlan.join(" | ") }
10463
+ });
10464
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
10465
+ console.log("Implementation test gate returned for review.");
10466
+ return { status: gateResult.outcome === "passed" ? "completed" : "failed", exitCode: gateResult.outcome === "passed" ? 0 : 1 };
10467
+ }
10468
+ const failedMutation = {
10469
+ status: "failed",
10470
+ runnerId,
10471
+ idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID()}`,
10472
+ tool: toolName,
10473
+ durationMs,
10474
+ ...sessionTelemetry,
10475
+ message: `${toolName} did not produce a valid implementation test gate result.`,
10476
+ ...gateError ? { error: gateError } : {}
10477
+ };
10478
+ const failedResult = await submitPrimaryRunnerResult(apiClient, {
10479
+ accountId: workItem.accountId,
10480
+ projectId,
10481
+ repositoryLinkId,
10482
+ runnerId,
10483
+ workItemId: workItem.workItemId,
10484
+ workKind: "implementationTestGate",
10485
+ resultKind: "implementationTestGateResult",
10486
+ attempt: workItem.attempt,
10487
+ idempotencyKey: failedMutation.idempotencyKey,
10488
+ result: failedMutation
10489
+ });
10490
+ if (!failedResult) return { status: "failed", exitCode: 1 };
10491
+ await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10492
+ await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10493
+ status: "failed",
10494
+ summary: gateError ?? `${toolName} did not produce a valid implementation test gate result.`,
10495
+ idempotencyKey: `runner_milestone_implementation_test_gate_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
10496
+ metadata: { tool: toolName, durationMs, verificationSummary: "Implementation test gate output did not include valid structured JSON." }
10497
+ });
10498
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
10499
+ console.error(gateError ?? "Local runner implementation test gate failed.");
10500
+ return { status: "failed", exitCode: toolResult.exitCode || 1 };
10501
+ }
8745
10502
  async function createRunnerWorkPrompt(apiClient, projectId, workItem) {
8746
10503
  if (workItem.workKind === "assistantQuestion") {
8747
10504
  const emptyProjectContext = { maps: [], refreshes: [], misses: [] };
@@ -8798,6 +10555,15 @@ async function createRunnerWorkPrompt(apiClient, projectId, workItem) {
8798
10555
  appEvaluationScan: { documents: documents2 }
8799
10556
  });
8800
10557
  }
10558
+ if (workItem.workKind === "testQualityScan") {
10559
+ const { documents: documents2 } = await apiClient.listBrainDocuments(projectId);
10560
+ return createWorkExecutionPrompt(workItem, {
10561
+ testQualityScan: { documents: documents2 }
10562
+ });
10563
+ }
10564
+ if (workItem.workKind === "implementationTestGate") {
10565
+ return createWorkExecutionPrompt(workItem);
10566
+ }
8801
10567
  if (workItem.workKind === "projectContextRefresh") {
8802
10568
  const emptyProjectContext = { maps: [], refreshes: [], misses: [] };
8803
10569
  const [{ documents: documents2 }, context] = await Promise.all([
@@ -9285,15 +11051,17 @@ function toRunnerToolCapabilities(tools) {
9285
11051
  supportsGitWorktreeIsolation: true
9286
11052
  }));
9287
11053
  }
9288
- function runnerIsolationCapabilityMetadata() {
11054
+ function runnerIsolationCapabilityMetadata(laneMetadata = {}) {
9289
11055
  return {
9290
11056
  machineId: runnerMachineId(),
9291
11057
  supportedWorkKinds: runnerSupportedWorkKinds,
9292
11058
  supportsBranchIsolation: true,
9293
- supportsGitWorktreeIsolation: true
11059
+ supportsGitWorktreeIsolation: true,
11060
+ ...laneMetadata.claimLaneId ? { claimLaneId: laneMetadata.claimLaneId } : {},
11061
+ ...laneMetadata.maxConcurrentWork !== void 0 ? { maxConcurrentWork: laneMetadata.maxConcurrentWork } : {}
9294
11062
  };
9295
11063
  }
9296
- function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode()) {
11064
+ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode(), concurrencyMetadata = { maxConcurrentWork: 1, activeClaimLaneIds: ["default"] }) {
9297
11065
  const modelConfig = toolConfig ? toolConfigModelOptions(toolConfig) : {};
9298
11066
  const effectiveModelConfig = toolConfig?.ready ? modelConfig : {};
9299
11067
  return {
@@ -9301,6 +11069,8 @@ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode()) {
9301
11069
  mode,
9302
11070
  hostname: os8.hostname(),
9303
11071
  ...runnerIsolationCapabilityMetadata(),
11072
+ maxConcurrentWork: concurrencyMetadata.maxConcurrentWork,
11073
+ activeClaimLaneIds: concurrencyMetadata.activeClaimLaneIds,
9304
11074
  resourceUsage: sampleCurrentRunnerResourceUsage(),
9305
11075
  ...toolConfig?.capabilities ? { capabilities: toolConfig.capabilities } : {},
9306
11076
  ...toolConfig?.requestedTool ? { requestedTool: toolConfig.requestedTool } : {},