@aviaratech/ai-delivery 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/CONTRIBUTING.md +47 -0
  2. package/LICENSE +21 -0
  3. package/README.md +84 -0
  4. package/dist/agent.d.ts +17 -0
  5. package/dist/agent.js +16 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +314 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/cliResult.d.ts +2 -0
  11. package/dist/cliResult.js +12 -0
  12. package/dist/cliResult.js.map +1 -0
  13. package/dist/cliResult.test.d.ts +1 -0
  14. package/dist/cliResult.test.js +30 -0
  15. package/dist/cliResult.test.js.map +1 -0
  16. package/dist/config/deliveryConfig.d.ts +113 -0
  17. package/dist/config/deliveryConfig.js +261 -0
  18. package/dist/config/deliveryConfig.js.map +1 -0
  19. package/dist/config/deliveryConfig.test.d.ts +1 -0
  20. package/dist/config/deliveryConfig.test.js +355 -0
  21. package/dist/config/deliveryConfig.test.js.map +1 -0
  22. package/dist/delivery/common.d.ts +28 -0
  23. package/dist/delivery/common.js +117 -0
  24. package/dist/delivery/common.js.map +1 -0
  25. package/dist/delivery/delivery.test.d.ts +1 -0
  26. package/dist/delivery/delivery.test.js +234 -0
  27. package/dist/delivery/delivery.test.js.map +1 -0
  28. package/dist/delivery/evidence.d.ts +92 -0
  29. package/dist/delivery/evidence.js +126 -0
  30. package/dist/delivery/evidence.js.map +1 -0
  31. package/dist/delivery/index.d.ts +8 -0
  32. package/dist/delivery/index.js +5 -0
  33. package/dist/delivery/index.js.map +1 -0
  34. package/dist/delivery/policy.d.ts +237 -0
  35. package/dist/delivery/policy.js +246 -0
  36. package/dist/delivery/policy.js.map +1 -0
  37. package/dist/delivery/stage.d.ts +125 -0
  38. package/dist/delivery/stage.js +218 -0
  39. package/dist/delivery/stage.js.map +1 -0
  40. package/dist/dispatch.d.ts +15 -0
  41. package/dist/dispatch.js +397 -0
  42. package/dist/dispatch.js.map +1 -0
  43. package/dist/errors.d.ts +4 -0
  44. package/dist/errors.js +9 -0
  45. package/dist/errors.js.map +1 -0
  46. package/dist/genericCompatibility.test.d.ts +1 -0
  47. package/dist/genericCompatibility.test.js +92 -0
  48. package/dist/genericCompatibility.test.js.map +1 -0
  49. package/dist/git.d.ts +13 -0
  50. package/dist/git.js +65 -0
  51. package/dist/git.js.map +1 -0
  52. package/dist/github/client.d.ts +34 -0
  53. package/dist/github/client.js +143 -0
  54. package/dist/github/client.js.map +1 -0
  55. package/dist/github/commandIdentityPolicy.d.ts +13 -0
  56. package/dist/github/commandIdentityPolicy.js +46 -0
  57. package/dist/github/commandIdentityPolicy.js.map +1 -0
  58. package/dist/github/nativeIssueMetadata.d.ts +78 -0
  59. package/dist/github/nativeIssueMetadata.js +249 -0
  60. package/dist/github/nativeIssueMetadata.js.map +1 -0
  61. package/dist/github/projectDelivery.d.ts +68 -0
  62. package/dist/github/projectDelivery.js +540 -0
  63. package/dist/github/projectDelivery.js.map +1 -0
  64. package/dist/github/relationships.d.ts +69 -0
  65. package/dist/github/relationships.js +395 -0
  66. package/dist/github/relationships.js.map +1 -0
  67. package/dist/github/repo.d.ts +8 -0
  68. package/dist/github/repo.js +34 -0
  69. package/dist/github/repo.js.map +1 -0
  70. package/dist/index.d.ts +2 -0
  71. package/dist/index.js +3 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/issue.d.ts +86 -0
  74. package/dist/issue.js +347 -0
  75. package/dist/issue.js.map +1 -0
  76. package/dist/lifecycle.test.d.ts +1 -0
  77. package/dist/lifecycle.test.js +1118 -0
  78. package/dist/lifecycle.test.js.map +1 -0
  79. package/dist/logger.d.ts +21 -0
  80. package/dist/logger.js +86 -0
  81. package/dist/logger.js.map +1 -0
  82. package/dist/mcp/index.d.ts +5 -0
  83. package/dist/mcp/index.js +46 -0
  84. package/dist/mcp/index.js.map +1 -0
  85. package/dist/mcp/tools.d.ts +162 -0
  86. package/dist/mcp/tools.js +154 -0
  87. package/dist/mcp/tools.js.map +1 -0
  88. package/dist/pr.d.ts +146 -0
  89. package/dist/pr.js +972 -0
  90. package/dist/pr.js.map +1 -0
  91. package/dist/pr.test.d.ts +1 -0
  92. package/dist/pr.test.js +252 -0
  93. package/dist/pr.test.js.map +1 -0
  94. package/dist/review.d.ts +104 -0
  95. package/dist/review.js +228 -0
  96. package/dist/review.js.map +1 -0
  97. package/dist/review.test.d.ts +1 -0
  98. package/dist/review.test.js +327 -0
  99. package/dist/review.test.js.map +1 -0
  100. package/dist/services/agentReadinessService.d.ts +17 -0
  101. package/dist/services/agentReadinessService.js +225 -0
  102. package/dist/services/agentReadinessService.js.map +1 -0
  103. package/dist/services/deliveryAdmission.d.ts +33 -0
  104. package/dist/services/deliveryAdmission.js +122 -0
  105. package/dist/services/deliveryAdmission.js.map +1 -0
  106. package/dist/services/deliveryRecordService.d.ts +47 -0
  107. package/dist/services/deliveryRecordService.js +199 -0
  108. package/dist/services/deliveryRecordService.js.map +1 -0
  109. package/dist/services/legacyIssueMigration.d.ts +46 -0
  110. package/dist/services/legacyIssueMigration.js +313 -0
  111. package/dist/services/legacyIssueMigration.js.map +1 -0
  112. package/dist/services/worktreeRegistry.d.ts +67 -0
  113. package/dist/services/worktreeRegistry.js +545 -0
  114. package/dist/services/worktreeRegistry.js.map +1 -0
  115. package/dist/terminalMessages.d.ts +13 -0
  116. package/dist/terminalMessages.js +43 -0
  117. package/dist/terminalMessages.js.map +1 -0
  118. package/dist/utils/atomicJson.d.ts +6 -0
  119. package/dist/utils/atomicJson.js +78 -0
  120. package/dist/utils/atomicJson.js.map +1 -0
  121. package/dist/utils/filesystem.d.ts +9 -0
  122. package/dist/utils/filesystem.js +19 -0
  123. package/dist/utils/filesystem.js.map +1 -0
  124. package/dist/utils/lockfile.d.ts +12 -0
  125. package/dist/utils/lockfile.js +78 -0
  126. package/dist/utils/lockfile.js.map +1 -0
  127. package/dist/verification.d.ts +24 -0
  128. package/dist/verification.js +227 -0
  129. package/dist/verification.js.map +1 -0
  130. package/dist/worktree.d.ts +44 -0
  131. package/dist/worktree.js +255 -0
  132. package/dist/worktree.js.map +1 -0
  133. package/package.json +82 -0
  134. package/plugins/ai-delivery/.claude-plugin/plugin.json +6 -0
  135. package/plugins/ai-delivery/.mcp.json +8 -0
  136. package/plugins/ai-delivery/README.md +5 -0
  137. package/plugins/ai-delivery/dist/mcp-launcher.js +22 -0
  138. package/plugins/ai-delivery/skills/intake-create/SKILL.md +26 -0
  139. package/plugins/ai-delivery/skills/pr-handoff/SKILL.md +28 -0
  140. package/plugins/ai-delivery/skills/worktree-lifecycle/SKILL.md +24 -0
package/dist/review.js ADDED
@@ -0,0 +1,228 @@
1
+ import { join } from 'node:path';
2
+ import { z } from 'zod';
3
+ import { assertPrivateFile } from './delivery/common.js';
4
+ import { digestValue, RepositoryApprovalBindingSchema, } from './delivery/index.js';
5
+ import { DeliveryError } from './errors.js';
6
+ import { gitCommonDir } from './git.js';
7
+ import { writePrivateJsonFileAtomically } from './utils/atomicJson.js';
8
+ const Sha = z.string().regex(/^[a-f0-9]{40}$/u);
9
+ const Digest = z.string().regex(/^sha256:[a-f0-9]{64}$/u);
10
+ export const ReviewArtifactSchema = z
11
+ .object({
12
+ artifactId: Digest,
13
+ authorIdentity: z.string().min(1),
14
+ checks: z.array(z.string().min(1)).min(1),
15
+ diffScopeHash: Digest,
16
+ deliveryMetrics: z
17
+ .object({ blockerTimeMs: z.number().int().nonnegative().nullable(), reviewRound: z.number().int().positive() })
18
+ .strict()
19
+ .optional(),
20
+ elapsedMs: z.number().int().nonnegative(),
21
+ findings: z.array(z.string()),
22
+ head: z.object({ sha: Sha, tree: Sha }).strict(),
23
+ issueNumber: z.number().int().positive(),
24
+ prNumber: z.number().int().positive().nullable(),
25
+ readOnly: z.literal(true),
26
+ requestedEffort: z.string().min(1),
27
+ requestedModel: z.string().min(1),
28
+ effectiveEffort: z.string().min(1),
29
+ effectiveModel: z.string().min(1),
30
+ reviewerIdentity: z.string().min(1),
31
+ schemaVersion: z.literal('ai-delivery.review-artifact@1'),
32
+ summary: z.string().min(1),
33
+ verdict: z.enum(['approve', 'request-changes']),
34
+ })
35
+ .strict()
36
+ .superRefine((artifact, ctx) => {
37
+ const { artifactId, ...content } = artifact;
38
+ if (artifactId !== digestValue(content) || artifact.authorIdentity === artifact.reviewerIdentity) {
39
+ ctx.addIssue({ code: 'custom', message: 'Review artifact identity or role separation is invalid.' });
40
+ }
41
+ });
42
+ export const SubmittedReviewReceiptSchema = z
43
+ .object({
44
+ artifact: ReviewArtifactSchema,
45
+ completedAt: z.iso.datetime(),
46
+ githubReviewId: z.number().int().positive(),
47
+ githubReviewUrl: z.url(),
48
+ headSha: Sha,
49
+ login: z.string().min(1),
50
+ prNumber: z.number().int().positive(),
51
+ publicationEvidenceId: Digest,
52
+ receiptId: Digest,
53
+ schemaVersion: z.literal('ai-delivery.review-receipt@1'),
54
+ state: z.literal('APPROVED'),
55
+ })
56
+ .strict()
57
+ .superRefine((receipt, ctx) => {
58
+ const { receiptId, ...content } = receipt;
59
+ if (receiptId !== digestValue(content) ||
60
+ receipt.headSha !== receipt.artifact.head.sha ||
61
+ (receipt.artifact.prNumber !== null && receipt.artifact.prNumber !== receipt.prNumber)) {
62
+ ctx.addIssue({ code: 'custom', message: 'Submitted review receipt is not bound to its artifact.' });
63
+ }
64
+ });
65
+ export function parseReviewArtifact(raw) {
66
+ const value = typeof raw === 'string' ? JSON.parse(raw) : raw;
67
+ return ReviewArtifactSchema.parse(value);
68
+ }
69
+ export function reviewArtifactApproval(input) {
70
+ const { artifact, classification, config } = input;
71
+ ReviewArtifactSchema.parse(artifact);
72
+ if (artifact.verdict !== 'approve' ||
73
+ artifact.issueNumber !== input.issueNumber ||
74
+ artifact.authorIdentity !== config.roles.author.identity ||
75
+ artifact.reviewerIdentity !== config.roles.reviewer.identity ||
76
+ artifact.head.sha !== classification.head.sha ||
77
+ artifact.head.tree !== classification.head.tree ||
78
+ artifact.diffScopeHash !== digestValue(classification.changedPaths) ||
79
+ (input.prNumber !== undefined && artifact.prNumber !== null && artifact.prNumber !== input.prNumber)) {
80
+ throw new DeliveryError('Independent review artifact is stale, foreign, rejected, or covers another scope.');
81
+ }
82
+ return RepositoryApprovalBindingSchema.parse({
83
+ authorIdentity: artifact.authorIdentity,
84
+ diffScopeHash: artifact.diffScopeHash,
85
+ head: artifact.head,
86
+ result: 'approved',
87
+ reviewerIdentity: artifact.reviewerIdentity,
88
+ reviewReceiptId: artifact.artifactId,
89
+ });
90
+ }
91
+ function receiptPath(repoRoot, issueNumber, headSha) {
92
+ return join(gitCommonDir(repoRoot), 'ai-delivery', 'reviews', String(issueNumber), `${headSha}.json`);
93
+ }
94
+ function prepublicationPath(repoRoot, issueNumber, headSha) {
95
+ return join(gitCommonDir(repoRoot), 'ai-delivery', 'reviews', String(issueNumber), `${headSha}.prepublication.json`);
96
+ }
97
+ export function savePrepublicationArtifact(input) {
98
+ if (input.artifact.prNumber !== null)
99
+ throw new DeliveryError('Prepublication review must have a null PR number.');
100
+ reviewArtifactApproval(input);
101
+ writePrivateJsonFileAtomically(prepublicationPath(input.repoRoot, input.issueNumber, input.artifact.head.sha), input.artifact);
102
+ }
103
+ export function loadPrepublicationArtifact(repoRoot, issueNumber, headSha) {
104
+ let raw;
105
+ try {
106
+ raw = JSON.parse(assertPrivateFile(prepublicationPath(repoRoot, issueNumber, headSha)).toString('utf8'));
107
+ }
108
+ catch (error) {
109
+ if (error.code === 'ENOENT')
110
+ return null;
111
+ throw new DeliveryError('Stored prepublication review is unreadable.');
112
+ }
113
+ return ReviewArtifactSchema.parse(raw);
114
+ }
115
+ async function listAllReviews(context, prNumber) {
116
+ const reviews = [];
117
+ for (let page = 1; page <= 20; page += 1) {
118
+ const result = await context.clients.rest.pulls.listReviews({
119
+ ...context.repo,
120
+ pull_number: prNumber,
121
+ per_page: 100,
122
+ page,
123
+ });
124
+ reviews.push(...result.data);
125
+ if (result.data.length < 100)
126
+ return reviews;
127
+ }
128
+ throw new DeliveryError('Review history exceeds the bounded readback window.');
129
+ }
130
+ export async function submitReview(input) {
131
+ const { artifact, context, issueNumber, prNumber } = input;
132
+ reviewArtifactApproval({
133
+ artifact,
134
+ classification: input.classification,
135
+ config: context.config,
136
+ issueNumber,
137
+ prNumber,
138
+ });
139
+ if (context.clients.authSource !== 'app' || context.clients.role !== 'reviewer' || !context.clients.appActorLogin) {
140
+ throw new DeliveryError('Formal review requires the reviewer GitHub App role.');
141
+ }
142
+ const pr = (await context.clients.rest.pulls.get({ ...context.repo, pull_number: prNumber })).data;
143
+ if (pr.head.sha !== artifact.head.sha || pr.state !== 'open')
144
+ throw new DeliveryError('PR head changed before formal review.');
145
+ const actor = await context.clients.appActorLogin();
146
+ if (!actor || !pr.user?.login || actor.toLowerCase() === pr.user.login.toLowerCase()) {
147
+ throw new DeliveryError('Reviewer GitHub actor must differ from PR author.');
148
+ }
149
+ const existing = loadSubmittedReview(context.root, issueNumber, artifact.head.sha);
150
+ if (existing) {
151
+ if (existing.artifact.artifactId !== artifact.artifactId ||
152
+ existing.prNumber !== prNumber ||
153
+ existing.publicationEvidenceId !== input.publicationEvidenceId ||
154
+ existing.login.toLowerCase() !== actor.toLowerCase()) {
155
+ throw new DeliveryError('An exact-head review receipt already binds different review evidence.');
156
+ }
157
+ await assertSubmittedReviewCurrent(context, existing);
158
+ return existing;
159
+ }
160
+ const marker = `<!-- ai-delivery-review-artifact: ${artifact.artifactId} -->`;
161
+ const matches = (await listAllReviews(context, prNumber)).filter((review) => review.body?.includes(marker));
162
+ if (matches.length > 1)
163
+ throw new DeliveryError('Multiple GitHub reviews claim the same artifact identity.');
164
+ const prior = matches[0];
165
+ if (prior &&
166
+ (prior.state !== 'APPROVED' ||
167
+ prior.commit_id !== artifact.head.sha ||
168
+ prior.user?.login?.toLowerCase() !== actor.toLowerCase() ||
169
+ !prior.html_url)) {
170
+ throw new DeliveryError('Prior GitHub review marker does not match exact approval.');
171
+ }
172
+ const submitted = prior ??
173
+ (await context.clients.rest.pulls.createReview({
174
+ ...context.repo,
175
+ pull_number: prNumber,
176
+ commit_id: artifact.head.sha,
177
+ body: `${artifact.summary}\n\n${marker}`,
178
+ event: 'APPROVE',
179
+ })).data;
180
+ if (!submitted.id ||
181
+ submitted.state !== 'APPROVED' ||
182
+ submitted.commit_id !== artifact.head.sha ||
183
+ submitted.user?.login?.toLowerCase() !== actor.toLowerCase() ||
184
+ !submitted.html_url) {
185
+ throw new DeliveryError('GitHub did not return complete exact-head approval readback.');
186
+ }
187
+ const content = {
188
+ artifact,
189
+ completedAt: new Date().toISOString(),
190
+ githubReviewId: submitted.id,
191
+ githubReviewUrl: submitted.html_url,
192
+ headSha: artifact.head.sha,
193
+ login: submitted.user.login,
194
+ prNumber,
195
+ publicationEvidenceId: input.publicationEvidenceId,
196
+ schemaVersion: 'ai-delivery.review-receipt@1',
197
+ state: 'APPROVED',
198
+ };
199
+ const receipt = SubmittedReviewReceiptSchema.parse({ ...content, receiptId: digestValue(content) });
200
+ await assertSubmittedReviewCurrent(context, receipt);
201
+ writePrivateJsonFileAtomically(receiptPath(context.root, issueNumber, artifact.head.sha), receipt);
202
+ return receipt;
203
+ }
204
+ export function loadSubmittedReview(repoRoot, issueNumber, headSha) {
205
+ let raw;
206
+ try {
207
+ raw = JSON.parse(assertPrivateFile(receiptPath(repoRoot, issueNumber, headSha)).toString('utf8'));
208
+ }
209
+ catch (error) {
210
+ if (error.code === 'ENOENT')
211
+ return null;
212
+ throw new DeliveryError('Stored review receipt is unreadable.');
213
+ }
214
+ return SubmittedReviewReceiptSchema.parse(raw);
215
+ }
216
+ export async function assertSubmittedReviewCurrent(context, receipt) {
217
+ SubmittedReviewReceiptSchema.parse(receipt);
218
+ const review = (await listAllReviews(context, receipt.prNumber)).find((candidate) => candidate.id === receipt.githubReviewId);
219
+ if (!review ||
220
+ review.state !== 'APPROVED' ||
221
+ review.commit_id !== receipt.headSha ||
222
+ review.user?.login !== receipt.login ||
223
+ review.html_url !== receipt.githubReviewUrl ||
224
+ !review.body?.includes(`<!-- ai-delivery-review-artifact: ${receipt.artifact.artifactId} -->`)) {
225
+ throw new DeliveryError('GitHub exact-head review no longer matches the stored receipt.');
226
+ }
227
+ }
228
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EACL,WAAW,EACX,+BAA+B,GAGhC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,OAAO,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAEvE,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;AAChD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC;KAClC,MAAM,CAAC;IACN,UAAU,EAAE,MAAM;IAClB,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACzC,aAAa,EAAE,MAAM;IACrB,eAAe,EAAE,CAAC;SACf,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;SAC9G,MAAM,EAAE;SACR,QAAQ,EAAE;IACb,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACzC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7B,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IAChD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACxC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChD,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IACzB,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACnC,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,+BAA+B,CAAC;IACzD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;CAChD,CAAC;KACD,MAAM,EAAE;KACR,WAAW,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE;IAC7B,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,GAAG,QAAQ,CAAC;IAC5C,IAAI,UAAU,KAAK,WAAW,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,cAAc,KAAK,QAAQ,CAAC,gBAAgB,EAAE,CAAC;QACjG,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,yDAAyD,EAAE,CAAC,CAAC;IACvG,CAAC;AACH,CAAC,CAAC,CAAC;AAGL,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC;KAC1C,MAAM,CAAC;IACN,QAAQ,EAAE,oBAAoB;IAC9B,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;IAC7B,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC3C,eAAe,EAAE,CAAC,CAAC,GAAG,EAAE;IACxB,OAAO,EAAE,GAAG;IACZ,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACrC,qBAAqB,EAAE,MAAM;IAC7B,SAAS,EAAE,MAAM;IACjB,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,8BAA8B,CAAC;IACxD,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;CAC7B,CAAC;KACD,MAAM,EAAE;KACR,WAAW,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;IAC5B,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,CAAC;IAC1C,IACE,SAAS,KAAK,WAAW,CAAC,OAAO,CAAC;QAClC,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG;QAC7C,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,KAAK,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ,CAAC,EACtF,CAAC;QACD,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,wDAAwD,EAAE,CAAC,CAAC;IACtG,CAAC;AACH,CAAC,CAAC,CAAC;AAGL,MAAM,UAAU,mBAAmB,CAAC,GAAY;IAC9C,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAa,CAAC,CAAC,CAAC,GAAG,CAAC;IAC3E,OAAO,oBAAoB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAMtC;IACC,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACnD,oBAAoB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrC,IACE,QAAQ,CAAC,OAAO,KAAK,SAAS;QAC9B,QAAQ,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW;QAC1C,QAAQ,CAAC,cAAc,KAAK,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ;QACxD,QAAQ,CAAC,gBAAgB,KAAK,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ;QAC5D,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,cAAc,CAAC,IAAI,CAAC,GAAG;QAC7C,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,IAAI,CAAC,IAAI;QAC/C,QAAQ,CAAC,aAAa,KAAK,WAAW,CAAC,cAAc,CAAC,YAAY,CAAC;QACnE,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,CAAC,EACpG,CAAC;QACD,MAAM,IAAI,aAAa,CAAC,mFAAmF,CAAC,CAAC;IAC/G,CAAC;IACD,OAAO,+BAA+B,CAAC,KAAK,CAAC;QAC3C,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,aAAa,EAAE,QAAQ,CAAC,aAAa;QACrC,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,MAAM,EAAE,UAAU;QAClB,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;QAC3C,eAAe,EAAE,QAAQ,CAAC,UAAU;KACrC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB,EAAE,WAAmB,EAAE,OAAe;IACzE,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,GAAG,OAAO,OAAO,CAAC,CAAC;AACxG,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAgB,EAAE,WAAmB,EAAE,OAAe;IAChF,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,GAAG,OAAO,sBAAsB,CAAC,CAAC;AACvH,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,KAM1C;IACC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,KAAK,IAAI;QAAE,MAAM,IAAI,aAAa,CAAC,mDAAmD,CAAC,CAAC;IACnH,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAC9B,8BAA8B,CAC5B,kBAAkB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAC9E,KAAK,CAAC,QAAQ,CACf,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,QAAgB,EAChB,WAAmB,EACnB,OAAe;IAEf,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAY,CAAC;IACtH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACpE,MAAM,IAAI,aAAa,CAAC,6CAA6C,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,oBAAoB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACzC,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,OAAwB,EAAE,QAAgB;IACtE,MAAM,OAAO,GAA+E,EAAE,CAAC;IAC/F,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;YAC1D,GAAG,OAAO,CAAC,IAAI;YACf,WAAW,EAAE,QAAQ;YACrB,QAAQ,EAAE,GAAG;YACb,IAAI;SACL,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG;YAAE,OAAO,OAAO,CAAC;IAC/C,CAAC;IACD,MAAM,IAAI,aAAa,CAAC,qDAAqD,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAOlC;IACC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAC3D,sBAAsB,CAAC;QACrB,QAAQ;QACR,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,WAAW;QACX,QAAQ;KACT,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,OAAO,CAAC,UAAU,KAAK,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAClH,MAAM,IAAI,aAAa,CAAC,sDAAsD,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,EAAE,GAAG,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACnG,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,KAAK,MAAM;QAC1D,MAAM,IAAI,aAAa,CAAC,uCAAuC,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;IACpD,IAAI,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;QACrF,MAAM,IAAI,aAAa,CAAC,mDAAmD,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnF,IAAI,QAAQ,EAAE,CAAC;QACb,IACE,QAAQ,CAAC,QAAQ,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU;YACpD,QAAQ,CAAC,QAAQ,KAAK,QAAQ;YAC9B,QAAQ,CAAC,qBAAqB,KAAK,KAAK,CAAC,qBAAqB;YAC9D,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,EACpD,CAAC;YACD,MAAM,IAAI,aAAa,CAAC,uEAAuE,CAAC,CAAC;QACnG,CAAC;QACD,MAAM,4BAA4B,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACtD,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,MAAM,GAAG,qCAAqC,QAAQ,CAAC,UAAU,MAAM,CAAC;IAC9E,MAAM,OAAO,GAAG,CAAC,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5G,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,aAAa,CAAC,2DAA2D,CAAC,CAAC;IAC7G,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzB,IACE,KAAK;QACL,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU;YACzB,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC,IAAI,CAAC,GAAG;YACrC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE;YACxD,CAAC,KAAK,CAAC,QAAQ,CAAC,EAClB,CAAC;QACD,MAAM,IAAI,aAAa,CAAC,2DAA2D,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,SAAS,GACb,KAAK;QACL,CACE,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC;YAC5C,GAAG,OAAO,CAAC,IAAI;YACf,WAAW,EAAE,QAAQ;YACrB,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG;YAC5B,IAAI,EAAE,GAAG,QAAQ,CAAC,OAAO,OAAO,MAAM,EAAE;YACxC,KAAK,EAAE,SAAS;SACjB,CAAC,CACH,CAAC,IAAI,CAAC;IACT,IACE,CAAC,SAAS,CAAC,EAAE;QACb,SAAS,CAAC,KAAK,KAAK,UAAU;QAC9B,SAAS,CAAC,SAAS,KAAK,QAAQ,CAAC,IAAI,CAAC,GAAG;QACzC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE;QAC5D,CAAC,SAAS,CAAC,QAAQ,EACnB,CAAC;QACD,MAAM,IAAI,aAAa,CAAC,8DAA8D,CAAC,CAAC;IAC1F,CAAC;IACD,MAAM,OAAO,GAAG;QACd,QAAQ;QACR,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,cAAc,EAAE,SAAS,CAAC,EAAE;QAC5B,eAAe,EAAE,SAAS,CAAC,QAAQ;QACnC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG;QAC1B,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK;QAC3B,QAAQ;QACR,qBAAqB,EAAE,KAAK,CAAC,qBAAqB;QAClD,aAAa,EAAE,8BAAuC;QACtD,KAAK,EAAE,UAAmB;KAC3B,CAAC;IACF,MAAM,OAAO,GAAG,4BAA4B,CAAC,KAAK,CAAC,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpG,MAAM,4BAA4B,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACrD,8BAA8B,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACnG,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,QAAgB,EAChB,WAAmB,EACnB,OAAe;IAEf,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAY,CAAC;IAC/G,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACpE,MAAM,IAAI,aAAa,CAAC,sCAAsC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,4BAA4B,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,OAAwB,EACxB,OAA+B;IAE/B,4BAA4B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,CAAC,MAAM,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CACnE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,OAAO,CAAC,cAAc,CACvD,CAAC;IACF,IACE,CAAC,MAAM;QACP,MAAM,CAAC,KAAK,KAAK,UAAU;QAC3B,MAAM,CAAC,SAAS,KAAK,OAAO,CAAC,OAAO;QACpC,MAAM,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,KAAK;QACpC,MAAM,CAAC,QAAQ,KAAK,OAAO,CAAC,eAAe;QAC3C,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,qCAAqC,OAAO,CAAC,QAAQ,CAAC,UAAU,MAAM,CAAC,EAC9F,CAAC;QACD,MAAM,IAAI,aAAa,CAAC,gEAAgE,CAAC,CAAC;IAC5F,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,327 @@
1
+ import assert from 'node:assert/strict';
2
+ import { execFileSync } from 'node:child_process';
3
+ import { generateKeyPairSync } from 'node:crypto';
4
+ import { chmodSync, existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { test } from 'vitest';
8
+ import { digestValue } from './delivery/index.js';
9
+ import { createDeliveryGitHubClients, withAuthorGitToken } from './github/client.js';
10
+ import { runAuthorGit } from './pr.js';
11
+ import { ReviewArtifactSchema, submitReview } from './review.js';
12
+ const head = { sha: 'a'.repeat(40), tree: 'b'.repeat(40) };
13
+ const publicationEvidenceId = `sha256:${'c'.repeat(64)}`;
14
+ const marker = (id) => `<!-- ai-delivery-review-artifact: ${id} -->`;
15
+ function fixture(input = {}) {
16
+ const root = mkdtempSync(join(tmpdir(), 'ai-delivery-review-'));
17
+ execFileSync('git', ['init', '-q', root]);
18
+ const artifactContent = {
19
+ authorIdentity: 'synthetic-author',
20
+ checks: ['build'],
21
+ diffScopeHash: digestValue(['change.ts']),
22
+ elapsedMs: 1,
23
+ findings: [],
24
+ head,
25
+ issueNumber: 17,
26
+ prNumber: 19,
27
+ readOnly: true,
28
+ requestedEffort: 'xhigh',
29
+ requestedModel: 'gpt-6-astra',
30
+ effectiveEffort: 'xhigh',
31
+ effectiveModel: 'gpt-6-astra',
32
+ reviewerIdentity: 'synthetic-reviewer',
33
+ schemaVersion: 'ai-delivery.review-artifact@1',
34
+ summary: 'Reviewed exact changes.',
35
+ verdict: 'approve',
36
+ };
37
+ const artifact = ReviewArtifactSchema.parse({ ...artifactContent, artifactId: digestValue(artifactContent) });
38
+ const review = {
39
+ id: 41,
40
+ state: 'APPROVED',
41
+ commit_id: head.sha,
42
+ user: { login: input.reviewLogin ?? 'synthetic-reviewer[bot]' },
43
+ html_url: 'https://github.com/example/repo/pull/19#pullrequestreview-41',
44
+ body: marker(artifact.artifactId),
45
+ };
46
+ const reviews = input.existingReview ? [review] : [];
47
+ let created = 0;
48
+ let actorLookups = 0;
49
+ let currentPrHead = head.sha;
50
+ const context = {
51
+ root,
52
+ repo: { owner: 'example', repo: 'repo' },
53
+ config: {
54
+ roles: { author: { identity: 'synthetic-author' }, reviewer: { identity: 'synthetic-reviewer' } },
55
+ },
56
+ clients: {
57
+ authSource: 'app',
58
+ role: 'reviewer',
59
+ appActorLogin: async () => {
60
+ actorLookups += 1;
61
+ return 'synthetic-reviewer[bot]';
62
+ },
63
+ rest: {
64
+ users: {
65
+ getAuthenticated: () => {
66
+ throw new Error('installation token cannot call GET /user');
67
+ },
68
+ },
69
+ pulls: {
70
+ get: async () => ({
71
+ data: {
72
+ head: { sha: currentPrHead },
73
+ state: 'open',
74
+ user: { login: input.authorLogin === undefined ? 'synthetic-author[bot]' : input.authorLogin },
75
+ },
76
+ }),
77
+ listReviews: async () => ({ data: reviews }),
78
+ createReview: async () => {
79
+ created += 1;
80
+ reviews.push(review);
81
+ return { data: review };
82
+ },
83
+ },
84
+ },
85
+ },
86
+ };
87
+ const classification = { head, changedPaths: ['change.ts'] };
88
+ const submit = () => submitReview({ artifact, classification, context, issueNumber: 17, prNumber: 19, publicationEvidenceId });
89
+ return {
90
+ root,
91
+ artifact,
92
+ reviews,
93
+ submit,
94
+ setPrHead(sha) {
95
+ currentPrHead = sha;
96
+ },
97
+ get created() {
98
+ return created;
99
+ },
100
+ get actorLookups() {
101
+ return actorLookups;
102
+ },
103
+ };
104
+ }
105
+ test('reviewer App bot submits and reuses an exact-head approval without GET /user', async () => {
106
+ const state = fixture();
107
+ try {
108
+ const first = await state.submit();
109
+ assert.equal(first.login, 'synthetic-reviewer[bot]');
110
+ assert.equal(state.created, 1);
111
+ const second = await state.submit();
112
+ assert.equal(second.receiptId, first.receiptId);
113
+ assert.equal(state.created, 1);
114
+ assert.equal(state.actorLookups, 2);
115
+ state.setPrHead('d'.repeat(40));
116
+ await assert.rejects(state.submit(), /PR head changed/u);
117
+ }
118
+ finally {
119
+ rmSync(state.root, { recursive: true, force: true });
120
+ }
121
+ });
122
+ test('reviewer App bot recovers a matching remote marker without another review', async () => {
123
+ const state = fixture({ existingReview: true });
124
+ try {
125
+ const receipt = await state.submit();
126
+ assert.equal(receipt.githubReviewId, 41);
127
+ assert.equal(state.created, 0);
128
+ }
129
+ finally {
130
+ rmSync(state.root, { recursive: true, force: true });
131
+ }
132
+ });
133
+ test('reviewer App rejects self review and a foreign marker actor', async () => {
134
+ const self = fixture({ authorLogin: 'synthetic-reviewer[bot]' });
135
+ const unknownAuthor = fixture({ authorLogin: null });
136
+ const foreign = fixture({ existingReview: true, reviewLogin: 'another-app[bot]' });
137
+ try {
138
+ await assert.rejects(self.submit(), /must differ from PR author/u);
139
+ await assert.rejects(unknownAuthor.submit(), /must differ from PR author/u);
140
+ await assert.rejects(foreign.submit(), /does not match exact approval/u);
141
+ assert.equal(self.created, 0);
142
+ assert.equal(unknownAuthor.created, 0);
143
+ assert.equal(foreign.created, 0);
144
+ }
145
+ finally {
146
+ rmSync(self.root, { recursive: true, force: true });
147
+ rmSync(unknownAuthor.root, { recursive: true, force: true });
148
+ rmSync(foreign.root, { recursive: true, force: true });
149
+ }
150
+ });
151
+ test('App client resolves its bot from JWT GET /app and keeps author token scoped', async () => {
152
+ const root = mkdtempSync(join(tmpdir(), 'ai-delivery-app-auth-'));
153
+ const originalFetch = globalThis.fetch;
154
+ const originalPath = process.env.PATH;
155
+ const originalSsh = process.env.GIT_SSH_COMMAND;
156
+ const originalGitConfig = process.env.GIT_CONFIG;
157
+ const originalGitTrace = process.env.GIT_TRACE;
158
+ const originalGhToken = process.env.GH_TOKEN;
159
+ const realGit = (originalPath ?? '')
160
+ .split(':')
161
+ .map((directory) => join(directory, 'git'))
162
+ .find(existsSync);
163
+ if (!realGit)
164
+ throw new Error('Git executable is unavailable for transport test.');
165
+ execFileSync(realGit, ['init', '-q', root]);
166
+ const privateKey = generateKeyPairSync('rsa', { modulusLength: 2048 }).privateKey.export({
167
+ format: 'pem',
168
+ type: 'pkcs8',
169
+ });
170
+ const authorKeyPath = join(root, 'author.pem');
171
+ const reviewerKeyPath = join(root, 'reviewer.pem');
172
+ writeFileSync(authorKeyPath, privateKey);
173
+ writeFileSync(reviewerKeyPath, privateKey);
174
+ const env = {
175
+ AUTHOR_APP_ID: '101',
176
+ AUTHOR_INSTALLATION_ID: '201',
177
+ AUTHOR_KEY_PATH: authorKeyPath,
178
+ REVIEWER_APP_ID: '102',
179
+ REVIEWER_INSTALLATION_ID: '202',
180
+ REVIEWER_KEY_PATH: reviewerKeyPath,
181
+ };
182
+ const config = {
183
+ roles: {
184
+ author: {
185
+ identity: 'synthetic-author',
186
+ credentialEnv: {
187
+ appId: 'AUTHOR_APP_ID',
188
+ installationId: 'AUTHOR_INSTALLATION_ID',
189
+ privateKeyPath: 'AUTHOR_KEY_PATH',
190
+ },
191
+ },
192
+ reviewer: {
193
+ identity: 'synthetic-reviewer',
194
+ credentialEnv: {
195
+ appId: 'REVIEWER_APP_ID',
196
+ installationId: 'REVIEWER_INSTALLATION_ID',
197
+ privateKeyPath: 'REVIEWER_KEY_PATH',
198
+ },
199
+ },
200
+ },
201
+ };
202
+ const requests = [];
203
+ let appReadbackOverride = null;
204
+ globalThis.fetch = async (url, init) => {
205
+ const requestUrl = new URL(url instanceof Request ? url.url : String(url));
206
+ const authorization = new Headers(init?.headers).get('authorization') ?? '';
207
+ requests.push(`${init?.method ?? 'GET'} ${requestUrl.pathname}`);
208
+ assert.match(authorization, /^bearer [^.]+\.[^.]+\.[^.]+$/u);
209
+ if (requestUrl.pathname === '/app') {
210
+ const payload = JSON.parse(Buffer.from(authorization.split('.')[1], 'base64url').toString());
211
+ const id = appReadbackOverride ?? Number(payload.iss);
212
+ return new Response(JSON.stringify({ id, slug: id === 101 ? 'synthetic-author' : 'synthetic-reviewer' }), {
213
+ status: 200,
214
+ headers: { 'content-type': 'application/json' },
215
+ });
216
+ }
217
+ assert.ok(['/app/installations/201/access_tokens', '/app/installations/202/access_tokens'].includes(requestUrl.pathname));
218
+ const isAuthor = requestUrl.pathname.includes('/201/');
219
+ return new Response(JSON.stringify({
220
+ token: isAuthor ? 'ghs_synthetic_author' : 'ghs_synthetic_reviewer',
221
+ expires_at: new Date(Date.now() + 3600000).toISOString(),
222
+ permissions: isAuthor
223
+ ? { contents: 'write', issues: 'write', organization_projects: 'write', pull_requests: 'write' }
224
+ : { contents: 'read', pull_requests: 'write' },
225
+ repository_selection: 'all',
226
+ }), {
227
+ status: 201,
228
+ headers: { 'content-type': 'application/json' },
229
+ });
230
+ };
231
+ try {
232
+ const clients = await createDeliveryGitHubClients({
233
+ config,
234
+ env,
235
+ identity: 'synthetic-reviewer',
236
+ role: 'reviewer',
237
+ });
238
+ assert.equal(await clients.appActorLogin?.(), 'synthetic-reviewer[bot]');
239
+ assert.deepEqual(requests, ['POST /app/installations/202/access_tokens', 'GET /app']);
240
+ await assert.rejects(withAuthorGitToken(clients, () => undefined), /selected author GitHub App/u);
241
+ const author = await createDeliveryGitHubClients({ config, env, identity: 'synthetic-author', role: 'author' });
242
+ assert.equal(await withAuthorGitToken(author, (token) => token), 'ghs_synthetic_author');
243
+ assert.deepEqual(requests, [
244
+ 'POST /app/installations/202/access_tokens',
245
+ 'GET /app',
246
+ 'POST /app/installations/201/access_tokens',
247
+ ]);
248
+ assert.deepEqual(await author.authenticatedAuthor?.(), {
249
+ actorLogin: 'synthetic-author[bot]',
250
+ credentialIdentity: 'app:101:installation:201',
251
+ });
252
+ if (!author.authenticatedAuthor)
253
+ throw new Error('Author identity readback is unavailable.');
254
+ appReadbackOverride = 102;
255
+ await assert.rejects(author.authenticatedAuthor(), /Author GitHub App identity did not match/u);
256
+ appReadbackOverride = null;
257
+ const shimDir = join(root, 'shim');
258
+ const marker = join(root, 'app-git-used');
259
+ mkdirSync(shimDir);
260
+ const shim = join(shimDir, 'git');
261
+ writeFileSync(shim, `#!/bin/sh
262
+ if [ "$1" = config ]; then exec "${realGit}" "$@"; fi
263
+ if [ "$1" = -c ] && [ "$2" = credential.helper= ] && [ "$3" = push ] &&
264
+ [ "$GIT_CONFIG_GLOBAL" = /dev/null ] && [ "$GIT_CONFIG_NOSYSTEM" = 1 ] &&
265
+ [ -z "$GIT_SSH_COMMAND" ] && [ -z "$GIT_CONFIG" ] && [ -z "$GIT_TRACE" ] &&
266
+ [ -z "$GH_TOKEN" ] && [ "$AI_DELIVERY_GIT_TOKEN" = ghs_synthetic_author ] &&
267
+ [ "$("$GIT_ASKPASS" Username)" = x-access-token ] &&
268
+ [ "$("$GIT_ASKPASS" Password)" = ghs_synthetic_author ]; then
269
+ : > "${marker}"
270
+ exit 0
271
+ fi
272
+ exit 97
273
+ `);
274
+ chmodSync(shim, 0o700);
275
+ process.env.PATH = `${shimDir}:${originalPath ?? ''}`;
276
+ process.env.GIT_SSH_COMMAND = 'ambient-ssh-identity';
277
+ process.env.GIT_CONFIG = '/dev/null';
278
+ process.env.GIT_TRACE = '1';
279
+ process.env.GH_TOKEN = 'synthetic-ambient-token';
280
+ const authorContext = { root, repo: { owner: 'example', repo: 'repo' }, clients: author };
281
+ await runAuthorGit(authorContext, root, ['push', 'https://github.com/example/repo.git', 'HEAD:refs/heads/issue/17'], 'push');
282
+ assert.equal(existsSync(marker), true);
283
+ delete process.env.GIT_CONFIG;
284
+ delete process.env.GIT_TRACE;
285
+ delete process.env.GH_TOKEN;
286
+ execFileSync(realGit, ['config', '--local', 'url.ssh://git@github.com/.pushInsteadOf', 'https://github.com/'], {
287
+ cwd: root,
288
+ });
289
+ process.env.GIT_CONFIG = '/dev/null';
290
+ await assert.rejects(runAuthorGit(authorContext, root, ['push', 'https://github.com/example/repo.git', 'HEAD:refs/heads/issue/17'], 'push'), /transport configuration/u);
291
+ delete process.env.GIT_CONFIG;
292
+ execFileSync(realGit, ['config', '--local', '--unset', 'url.ssh://git@github.com/.pushInsteadOf'], { cwd: root });
293
+ execFileSync(realGit, ['config', '--local', 'http.extraHeader', 'Authorization: Basic synthetic'], { cwd: root });
294
+ await assert.rejects(runAuthorGit(authorContext, root, ['push', 'https://github.com/example/repo.git', 'HEAD:refs/heads/issue/17'], 'push'), /transport configuration/u);
295
+ execFileSync(realGit, ['config', '--local', '--unset', 'http.extraHeader'], { cwd: root });
296
+ execFileSync(realGit, ['config', '--local', 'extensions.worktreeConfig', 'true'], { cwd: root });
297
+ execFileSync(realGit, ['config', '--worktree', 'url.ssh://git@github.com/.pushInsteadOf', 'https://github.com/'], {
298
+ cwd: root,
299
+ });
300
+ await assert.rejects(runAuthorGit(authorContext, root, ['push', 'https://github.com/example/repo.git', 'HEAD:refs/heads/issue/17'], 'push'), /transport configuration/u);
301
+ }
302
+ finally {
303
+ globalThis.fetch = originalFetch;
304
+ if (originalPath === undefined)
305
+ delete process.env.PATH;
306
+ else
307
+ process.env.PATH = originalPath;
308
+ if (originalSsh === undefined)
309
+ delete process.env.GIT_SSH_COMMAND;
310
+ else
311
+ process.env.GIT_SSH_COMMAND = originalSsh;
312
+ if (originalGitConfig === undefined)
313
+ delete process.env.GIT_CONFIG;
314
+ else
315
+ process.env.GIT_CONFIG = originalGitConfig;
316
+ if (originalGitTrace === undefined)
317
+ delete process.env.GIT_TRACE;
318
+ else
319
+ process.env.GIT_TRACE = originalGitTrace;
320
+ if (originalGhToken === undefined)
321
+ delete process.env.GH_TOKEN;
322
+ else
323
+ process.env.GH_TOKEN = originalGhToken;
324
+ rmSync(root, { recursive: true, force: true });
325
+ }
326
+ });
327
+ //# sourceMappingURL=review.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.test.js","sourceRoot":"","sources":["../src/review.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/F,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAG9B,OAAO,EAAE,WAAW,EAAwC,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAErF,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AAC3D,MAAM,qBAAqB,GAAG,UAAU,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AACzD,MAAM,MAAM,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,qCAAqC,EAAE,MAAM,CAAC;AAE7E,SAAS,OAAO,CAAC,QAAyF,EAAE;IAC1G,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAChE,YAAY,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAC1C,MAAM,eAAe,GAAG;QACtB,cAAc,EAAE,kBAAkB;QAClC,MAAM,EAAE,CAAC,OAAO,CAAC;QACjB,aAAa,EAAE,WAAW,CAAC,CAAC,WAAW,CAAC,CAAC;QACzC,SAAS,EAAE,CAAC;QACZ,QAAQ,EAAE,EAAE;QACZ,IAAI;QACJ,WAAW,EAAE,EAAE;QACf,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,IAAa;QACvB,eAAe,EAAE,OAAO;QACxB,cAAc,EAAE,aAAa;QAC7B,eAAe,EAAE,OAAO;QACxB,cAAc,EAAE,aAAa;QAC7B,gBAAgB,EAAE,oBAAoB;QACtC,aAAa,EAAE,+BAAwC;QACvD,OAAO,EAAE,yBAAyB;QAClC,OAAO,EAAE,SAAkB;KAC5B,CAAC;IACF,MAAM,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,EAAE,GAAG,eAAe,EAAE,UAAU,EAAE,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IAC9G,MAAM,MAAM,GAAG;QACb,EAAE,EAAE,EAAE;QACN,KAAK,EAAE,UAAU;QACjB,SAAS,EAAE,IAAI,CAAC,GAAG;QACnB,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,IAAI,yBAAyB,EAAE;QAC/D,QAAQ,EAAE,8DAA8D;QACxE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;KAClC,CAAC;IACF,MAAM,OAAO,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACrD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC;IAC7B,MAAM,OAAO,GAAG;QACd,IAAI;QACJ,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE;QACxC,MAAM,EAAE;YACN,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,kBAAkB,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,oBAAoB,EAAE,EAAE;SAChF;QACnB,OAAO,EAAE;YACP,UAAU,EAAE,KAAK;YACjB,IAAI,EAAE,UAAU;YAChB,aAAa,EAAE,KAAK,IAAI,EAAE;gBACxB,YAAY,IAAI,CAAC,CAAC;gBAClB,OAAO,yBAAyB,CAAC;YACnC,CAAC;YACD,IAAI,EAAE;gBACJ,KAAK,EAAE;oBACL,gBAAgB,EAAE,GAAG,EAAE;wBACrB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;oBAC9D,CAAC;iBACF;gBACD,KAAK,EAAE;oBACL,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;wBAChB,IAAI,EAAE;4BACJ,IAAI,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE;4BAC5B,KAAK,EAAE,MAAM;4BACb,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE;yBAC/F;qBACF,CAAC;oBACF,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;oBAC5C,YAAY,EAAE,KAAK,IAAI,EAAE;wBACvB,OAAO,IAAI,CAAC,CAAC;wBACb,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;wBACrB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oBAC1B,CAAC;iBACF;aACF;SACF;KAC4B,CAAC;IAChC,MAAM,cAAc,GAAG,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,WAAW,CAAC,EAAqC,CAAC;IAChG,MAAM,MAAM,GAAG,GAAG,EAAE,CAClB,YAAY,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAC5G,OAAO;QACL,IAAI;QACJ,QAAQ;QACR,OAAO;QACP,MAAM;QACN,SAAS,CAAC,GAAW;YACnB,aAAa,GAAG,GAAG,CAAC;QACtB,CAAC;QACD,IAAI,OAAO;YACT,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,IAAI,YAAY;YACd,OAAO,YAAY,CAAC;QACtB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,IAAI,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;IAC9F,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,yBAAyB,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QACpC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAChC,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC;IAC3D,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;IAC3F,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACjC,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;IAC7E,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC,CAAC;IACjE,MAAM,aAAa,GAAG,OAAO,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,OAAO,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC,CAAC;IACnF,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,6BAA6B,CAAC,CAAC;QACnE,MAAM,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,6BAA6B,CAAC,CAAC;QAC5E,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,gCAAgC,CAAC,CAAC;QACzE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACnC,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;IAC7F,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,uBAAuB,CAAC,CAAC,CAAC;IAClE,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC;IACvC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IACtC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAChD,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IACjD,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;IAC/C,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC7C,MAAM,OAAO,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;SACjC,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;SAC1C,IAAI,CAAC,UAAU,CAAC,CAAC;IACpB,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACnF,YAAY,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,mBAAmB,CAAC,KAAK,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC;QACvF,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,OAAO;KACd,CAAC,CAAC;IACH,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAC/C,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IACnD,aAAa,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IACzC,aAAa,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG;QACV,aAAa,EAAE,KAAK;QACpB,sBAAsB,EAAE,KAAK;QAC7B,eAAe,EAAE,aAAa;QAC9B,eAAe,EAAE,KAAK;QACtB,wBAAwB,EAAE,KAAK;QAC/B,iBAAiB,EAAE,eAAe;KACnC,CAAC;IACF,MAAM,MAAM,GAAG;QACb,KAAK,EAAE;YACL,MAAM,EAAE;gBACN,QAAQ,EAAE,kBAAkB;gBAC5B,aAAa,EAAE;oBACb,KAAK,EAAE,eAAe;oBACtB,cAAc,EAAE,wBAAwB;oBACxC,cAAc,EAAE,iBAAiB;iBAClC;aACF;YACD,QAAQ,EAAE;gBACR,QAAQ,EAAE,oBAAoB;gBAC9B,aAAa,EAAE;oBACb,KAAK,EAAE,iBAAiB;oBACxB,cAAc,EAAE,0BAA0B;oBAC1C,cAAc,EAAE,mBAAmB;iBACpC;aACF;SACF;KACgB,CAAC;IACpB,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,mBAAmB,GAAkB,IAAI,CAAC;IAC9C,UAAU,CAAC,KAAK,GAAG,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACrC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,GAAG,YAAY,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3E,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;QAC5E,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;QACjE,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,+BAA+B,CAAC,CAAC;QAC7D,IAAI,UAAU,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,EAAE,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAoB,CAAC;YACjH,MAAM,EAAE,GAAG,mBAAmB,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACtD,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,oBAAoB,EAAE,CAAC,EAAE;gBACxG,MAAM,EAAE,GAAG;gBACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;aAChD,CAAC,CAAC;QACL,CAAC;QACD,MAAM,CAAC,EAAE,CACP,CAAC,sCAAsC,EAAE,sCAAsC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,CAC/G,CAAC;QACF,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACvD,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;YACb,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,wBAAwB;YACnE,UAAU,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,WAAW,EAAE;YACxD,WAAW,EAAE,QAAQ;gBACnB,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE;gBAChG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE;YAChD,oBAAoB,EAAE,KAAK;SAC5B,CAAC,EACF;YACE,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAChD,CACF,CAAC;IACJ,CAAC,CAAC;IACF,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,2BAA2B,CAAC;YAChD,MAAM;YACN,GAAG;YACH,QAAQ,EAAE,oBAAoB;YAC9B,IAAI,EAAE,UAAU;SACjB,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,MAAM,OAAO,CAAC,aAAa,EAAE,EAAE,EAAE,yBAAyB,CAAC,CAAC;QACzE,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,2CAA2C,EAAE,UAAU,CAAC,CAAC,CAAC;QACtF,MAAM,MAAM,CAAC,OAAO,CAClB,kBAAkB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,EAC5C,6BAA6B,CAC9B,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,2BAA2B,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,kBAAkB,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChH,MAAM,CAAC,KAAK,CAAC,MAAM,kBAAkB,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,sBAAsB,CAAC,CAAC;QACzF,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE;YACzB,2CAA2C;YAC3C,UAAU;YACV,2CAA2C;SAC5C,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,MAAM,MAAM,CAAC,mBAAmB,EAAE,EAAE,EAAE;YACrD,UAAU,EAAE,uBAAuB;YACnC,kBAAkB,EAAE,0BAA0B;SAC/C,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,mBAAmB;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC7F,mBAAmB,GAAG,GAAG,CAAC;QAC1B,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,mBAAmB,EAAE,EAAE,2CAA2C,CAAC,CAAC;QAChG,mBAAmB,GAAG,IAAI,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAC1C,SAAS,CAAC,OAAO,CAAC,CAAC;QACnB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAClC,aAAa,CACX,IAAI,EACJ;mCAC6B,OAAO;;;;;;;SAOjC,MAAM;;;;CAId,CACI,CAAC;QACF,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,OAAO,IAAI,YAAY,IAAI,EAAE,EAAE,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,sBAAsB,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,WAAW,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,yBAAyB,CAAC;QACjD,MAAM,aAAa,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAqB,CAAC;QAC7G,MAAM,YAAY,CAChB,aAAa,EACb,IAAI,EACJ,CAAC,MAAM,EAAE,qCAAqC,EAAE,0BAA0B,CAAC,EAC3E,MAAM,CACP,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;QACvC,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;QAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QAC7B,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC5B,YAAY,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,yCAAyC,EAAE,qBAAqB,CAAC,EAAE;YAC7G,GAAG,EAAE,IAAI;SACV,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,WAAW,CAAC;QACrC,MAAM,MAAM,CAAC,OAAO,CAClB,YAAY,CACV,aAAa,EACb,IAAI,EACJ,CAAC,MAAM,EAAE,qCAAqC,EAAE,0BAA0B,CAAC,EAC3E,MAAM,CACP,EACD,0BAA0B,CAC3B,CAAC;QACF,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;QAC9B,YAAY,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,yCAAyC,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QAClH,YAAY,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,kBAAkB,EAAE,gCAAgC,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QAClH,MAAM,MAAM,CAAC,OAAO,CAClB,YAAY,CACV,aAAa,EACb,IAAI,EACJ,CAAC,MAAM,EAAE,qCAAqC,EAAE,0BAA0B,CAAC,EAC3E,MAAM,CACP,EACD,0BAA0B,CAC3B,CAAC;QACF,YAAY,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,kBAAkB,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3F,YAAY,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,2BAA2B,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QACjG,YAAY,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,yCAAyC,EAAE,qBAAqB,CAAC,EAAE;YAChH,GAAG,EAAE,IAAI;SACV,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAClB,YAAY,CACV,aAAa,EACb,IAAI,EACJ,CAAC,MAAM,EAAE,qCAAqC,EAAE,0BAA0B,CAAC,EAC3E,MAAM,CACP,EACD,0BAA0B,CAC3B,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,UAAU,CAAC,KAAK,GAAG,aAAa,CAAC;QACjC,IAAI,YAAY,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;;YACnD,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,YAAY,CAAC;QACrC,IAAI,WAAW,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;;YAC7D,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,WAAW,CAAC;QAC/C,IAAI,iBAAiB,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;;YAC9D,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,iBAAiB,CAAC;QAChD,IAAI,gBAAgB,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;;YAC5D,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,gBAAgB,CAAC;QAC9C,IAAI,eAAe,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;;YAC1D,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,eAAe,CAAC;QAC5C,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { NativePointValue } from '../github/nativeIssueMetadata.js';
2
+ export interface AgentReadinessResult {
3
+ failures: {
4
+ code: ReadinessFailureCode;
5
+ message: string;
6
+ }[];
7
+ ready: boolean;
8
+ }
9
+ export type ReadinessFailureCode = 'acceptance-criteria' | 'blocked' | 'evidence' | 'outcome' | 'points' | 'scope' | 'tracking-parent' | 'unresolved' | 'verification';
10
+ export declare function evaluateAgentReadiness(input: {
11
+ blockedBy?: number[];
12
+ body: string;
13
+ points?: NativePointValue;
14
+ repoRoot: string;
15
+ title: string;
16
+ trackingParent?: boolean;
17
+ }): AgentReadinessResult;