@aviaratech/ai-delivery 0.3.22 → 0.3.24

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 (131) hide show
  1. package/CONTRIBUTING.md +9 -0
  2. package/README.md +77 -442
  3. package/dist/agent.d.ts +5 -11
  4. package/dist/agent.js +4 -8
  5. package/dist/agent.js.map +1 -1
  6. package/dist/cli.js +59 -107
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config/deliveryConfig.d.ts +44 -55
  9. package/dist/config/deliveryConfig.js +85 -88
  10. package/dist/config/deliveryConfig.js.map +1 -1
  11. package/dist/config/deliveryConfig.test.js +13 -5
  12. package/dist/config/deliveryConfig.test.js.map +1 -1
  13. package/dist/delivery/common.d.ts +4 -1
  14. package/dist/delivery/common.js +56 -8
  15. package/dist/delivery/common.js.map +1 -1
  16. package/dist/delivery/delivery.test.js +2 -3
  17. package/dist/delivery/delivery.test.js.map +1 -1
  18. package/dist/delivery/index.d.ts +0 -6
  19. package/dist/delivery/index.js +0 -3
  20. package/dist/delivery/index.js.map +1 -1
  21. package/dist/delivery/legacy.d.ts +8 -0
  22. package/dist/delivery/legacy.js +5 -0
  23. package/dist/delivery/legacy.js.map +1 -0
  24. package/dist/delivery/policy.js +2 -13
  25. package/dist/delivery/policy.js.map +1 -1
  26. package/dist/delivery/stage.d.ts +6 -0
  27. package/dist/delivery/stage.js +11 -2
  28. package/dist/delivery/stage.js.map +1 -1
  29. package/dist/directoryIndependent.test.d.ts +1 -0
  30. package/dist/directoryIndependent.test.js +262 -0
  31. package/dist/directoryIndependent.test.js.map +1 -0
  32. package/dist/dispatch.d.ts +29 -3
  33. package/dist/dispatch.js +160 -457
  34. package/dist/dispatch.js.map +1 -1
  35. package/dist/genericCompatibility.test.js +0 -35
  36. package/dist/genericCompatibility.test.js.map +1 -1
  37. package/dist/git.js +10 -3
  38. package/dist/git.js.map +1 -1
  39. package/dist/gitProcess.d.ts +6 -0
  40. package/dist/gitProcess.js +56 -0
  41. package/dist/gitProcess.js.map +1 -0
  42. package/dist/github/discovery.d.ts +1 -0
  43. package/dist/github/discovery.js +1 -1
  44. package/dist/github/discovery.js.map +1 -1
  45. package/dist/github/discovery.test.js +25 -20
  46. package/dist/github/discovery.test.js.map +1 -1
  47. package/dist/github/nativeIssueMetadata.d.ts +2 -2
  48. package/dist/github/nativeIssueMetadata.js.map +1 -1
  49. package/dist/github/projectDelivery.d.ts +1 -1
  50. package/dist/github/projectDelivery.js.map +1 -1
  51. package/dist/github/repo.d.ts +7 -1
  52. package/dist/github/repo.js +58 -2
  53. package/dist/github/repo.js.map +1 -1
  54. package/dist/issue.d.ts +11 -6
  55. package/dist/issue.js +136 -319
  56. package/dist/issue.js.map +1 -1
  57. package/dist/issueJournal.d.ts +1 -1
  58. package/dist/lifecycle.test.js +40 -7371
  59. package/dist/lifecycle.test.js.map +1 -1
  60. package/dist/logger.js +4 -4
  61. package/dist/logger.js.map +1 -1
  62. package/dist/mcp/index.js +2 -1
  63. package/dist/mcp/index.js.map +1 -1
  64. package/dist/mcp/tools.d.ts +27 -42
  65. package/dist/mcp/tools.js +34 -42
  66. package/dist/mcp/tools.js.map +1 -1
  67. package/dist/pluginInstaller.d.ts +51 -0
  68. package/dist/pluginInstaller.js +774 -0
  69. package/dist/pluginInstaller.js.map +1 -0
  70. package/dist/pluginInstaller.test.d.ts +1 -0
  71. package/dist/pluginInstaller.test.js +543 -0
  72. package/dist/pluginInstaller.test.js.map +1 -0
  73. package/dist/pluginPackaging.test.d.ts +1 -0
  74. package/dist/pluginPackaging.test.js +245 -0
  75. package/dist/pluginPackaging.test.js.map +1 -0
  76. package/dist/pr.d.ts +66 -114
  77. package/dist/pr.js +288 -1108
  78. package/dist/pr.js.map +1 -1
  79. package/dist/pr.test.js +295 -237
  80. package/dist/pr.test.js.map +1 -1
  81. package/dist/releaseReconciliation.test.d.ts +1 -0
  82. package/dist/releaseReconciliation.test.js +314 -0
  83. package/dist/releaseReconciliation.test.js.map +1 -0
  84. package/dist/review.d.ts +37 -7
  85. package/dist/review.js +106 -45
  86. package/dist/review.js.map +1 -1
  87. package/dist/review.test.js +30 -71
  88. package/dist/review.test.js.map +1 -1
  89. package/dist/services/agentReadinessService.d.ts +1 -1
  90. package/dist/services/agentReadinessService.js +2 -0
  91. package/dist/services/agentReadinessService.js.map +1 -1
  92. package/dist/services/deliveryAdmission.d.ts +4 -4
  93. package/dist/services/deliveryAdmission.js.map +1 -1
  94. package/dist/setup.d.ts +1 -0
  95. package/dist/setup.js +71 -10
  96. package/dist/setup.js.map +1 -1
  97. package/dist/setup.test.js +594 -204
  98. package/dist/setup.test.js.map +1 -1
  99. package/dist/start.test.d.ts +1 -0
  100. package/dist/start.test.js +72 -0
  101. package/dist/start.test.js.map +1 -0
  102. package/dist/stdoutRegression.test.d.ts +1 -0
  103. package/dist/stdoutRegression.test.js +326 -0
  104. package/dist/stdoutRegression.test.js.map +1 -0
  105. package/dist/verification.d.ts +62 -27
  106. package/dist/verification.js +172 -1337
  107. package/dist/verification.js.map +1 -1
  108. package/dist/worktree.d.ts +1 -31
  109. package/dist/worktree.js +5 -200
  110. package/dist/worktree.js.map +1 -1
  111. package/dist/worktreeTransition.d.ts +1 -1
  112. package/dist/worktreeTransition.js +10 -5
  113. package/dist/worktreeTransition.js.map +1 -1
  114. package/dist/worktreeTransition.test.js +2 -3
  115. package/dist/worktreeTransition.test.js.map +1 -1
  116. package/package.json +4 -4
  117. package/plugins/ai-delivery/.claude-plugin/plugin.json +6 -1
  118. package/plugins/ai-delivery/LICENSE +21 -0
  119. package/plugins/ai-delivery/README.md +4 -2
  120. package/plugins/ai-delivery/dist/mcp-launcher.js +19 -8
  121. package/plugins/ai-delivery/mcp.json +10 -0
  122. package/plugins/ai-delivery/plugin.json +10 -0
  123. package/plugins/ai-delivery/runtime/dist/THIRD-PARTY-NOTICES.md +1010 -0
  124. package/plugins/ai-delivery/runtime/dist/cli.js +276 -0
  125. package/plugins/ai-delivery/runtime/package.json +13 -0
  126. package/plugins/ai-delivery/skills/intake-create/SKILL.md +1 -1
  127. package/plugins/ai-delivery/skills/pr-handoff/SKILL.md +14 -21
  128. package/plugins/ai-delivery/skills/worktree-lifecycle/SKILL.md +12 -23
  129. package/dist/services/deliveryRecordService.d.ts +0 -47
  130. package/dist/services/deliveryRecordService.js +0 -199
  131. package/dist/services/deliveryRecordService.js.map +0 -1
@@ -0,0 +1,314 @@
1
+ import assert from 'node:assert/strict';
2
+ import { createHash } from 'node:crypto';
3
+ import { readFileSync } from 'node:fs';
4
+ import { Script } from 'node:vm';
5
+ import { test } from 'vitest';
6
+ const workflow = readFileSync(new URL('../.github/workflows/release.yml', import.meta.url), 'utf8');
7
+ const scripts = [...workflow.matchAll(/node --input-type=module <<'JS'\n([\s\S]*?)\n\s+JS/gu)];
8
+ const publishedScript = scripts.at(-1)?.[1];
9
+ assert.ok(publishedScript, 'The actual release workflow must contain its publication script.');
10
+ // Substitute only external imports; all publication decisions and guards execute unchanged.
11
+ const program = publishedScript.replace(/^\s*import .+;\n/gmu, '');
12
+ const archive = Buffer.from('independently reviewed synthetic archive');
13
+ const digest = createHash('sha256').update(archive).digest('hex');
14
+ const integrity = `sha512-${createHash('sha512').update(archive).digest('base64')}`;
15
+ const source = 'a'.repeat(40);
16
+ const registry = 'https://registry.npmjs.org';
17
+ const packageUrl = `${registry}/@aviaratech%2fai-delivery`;
18
+ const version = '0.8.1';
19
+ const pkg = {
20
+ name: '@aviaratech/ai-delivery',
21
+ version,
22
+ private: false,
23
+ license: 'MIT',
24
+ repository: { url: 'git+https://github.com/aviaratech/ai-delivery.git' },
25
+ };
26
+ const manifest = {
27
+ name: pkg.name,
28
+ version,
29
+ dist: {
30
+ integrity,
31
+ tarball: `${registry}/@aviaratech/ai-delivery/-/ai-delivery-${version}.tgz`,
32
+ attestations: { provenance: { predicateType: 'https://slsa.dev/provenance/v1' } },
33
+ },
34
+ };
35
+ async function runRelease(fixture = {}) {
36
+ let publications = 0;
37
+ let reads = 0;
38
+ let elapsed = 0;
39
+ const requests = [];
40
+ const commands = [];
41
+ const waits = [];
42
+ const timeouts = [];
43
+ const messages = [];
44
+ const ready = (at = 1) => reads >= at;
45
+ const published = () => fixture.occupied || publications > 0;
46
+ const visibleManifest = (indexed = false) => {
47
+ const value = structuredClone(manifest);
48
+ if (!ready(fixture.provenanceReadyAt))
49
+ Reflect.deleteProperty(value.dist, 'attestations');
50
+ if (fixture.conflict === 'name')
51
+ value.name = '@someone/else';
52
+ if (fixture.conflict === 'version')
53
+ value.version = '0.8.0';
54
+ if (fixture.conflict === 'integrity' || (indexed && fixture.conflict === 'index'))
55
+ value.dist.integrity = 'sha512-conflicting';
56
+ if (fixture.conflict === 'provenance')
57
+ value.dist.attestations = { provenance: { predicateType: 'https://unexpected.example/predicate' } };
58
+ if (fixture.conflict === 'origin')
59
+ value.dist.tarball = 'https://unexpected.example/archive.tgz';
60
+ if (fixture.conflict === 'credentials') {
61
+ const tarball = new URL('/archive.tgz', registry);
62
+ tarball.username = 'user';
63
+ tarball.password = 'password';
64
+ value.dist.tarball = tarball.href;
65
+ }
66
+ return value;
67
+ };
68
+ const fetch = async (input, options = {}) => {
69
+ const url = String(input);
70
+ requests.push({ url, method: options.method ?? 'GET', at: elapsed });
71
+ assert.equal(options.redirect, 'error');
72
+ elapsed += fixture.fetchDurationMs ?? 100;
73
+ const phase = url.includes('api.github.com')
74
+ ? 'main'
75
+ : url === packageUrl
76
+ ? 'index'
77
+ : url === `${packageUrl}/${version}`
78
+ ? 'version'
79
+ : 'archive';
80
+ if (phase === fixture.errorPhase && (published() || phase === 'main')) {
81
+ if (fixture.networkFailure)
82
+ throw new Error('synthetic network failure');
83
+ return new Response(null, { status: fixture.errorStatus ?? 503 });
84
+ }
85
+ if (url === 'https://api.github.com/repos/aviaratech/ai-delivery/branches/main') {
86
+ return Response.json({
87
+ name: 'main',
88
+ protected: true,
89
+ commit: { sha: fixture.conflict === 'main' ? 'b'.repeat(40) : source },
90
+ protection: { required_status_checks: { enforcement_level: 'everyone', contexts: ['checks', 'secrets'] } },
91
+ });
92
+ }
93
+ if (url === `${packageUrl}/${version}`) {
94
+ if (published())
95
+ reads++;
96
+ return published() && ready(fixture.versionReadyAt)
97
+ ? fixture.oversizedManifest
98
+ ? new Response('x'.repeat(4 * 1024 * 1024 + 1))
99
+ : Response.json(visibleManifest())
100
+ : new Response(null, { status: 404 });
101
+ }
102
+ if (url === manifest.dist.tarball) {
103
+ if (fixture.bodyDeadlineExceeded) {
104
+ return new Response(new ReadableStream({
105
+ start: (controller) => {
106
+ elapsed += 5 * 60_000;
107
+ controller.error(new DOMException('Readback body timed out', 'TimeoutError'));
108
+ },
109
+ }));
110
+ }
111
+ return ready(fixture.archiveReadyAt)
112
+ ? new Response(fixture.corruptArchive ? Buffer.from('conflicting registry bytes') : archive)
113
+ : new Response(null, { status: 404 });
114
+ }
115
+ if (url === packageUrl) {
116
+ return Response.json({
117
+ name: pkg.name,
118
+ 'dist-tags': {
119
+ latest: published() && fixture.conflict === 'latest'
120
+ ? '0.8.2'
121
+ : published() && ready(fixture.latestReadyAt)
122
+ ? version
123
+ : '0.8.0',
124
+ },
125
+ versions: published() && ready(fixture.indexReadyAt) ? { [version]: visibleManifest(true) } : {},
126
+ });
127
+ }
128
+ throw new Error(`Unexpected external request: ${url}`);
129
+ };
130
+ const context = {
131
+ createHash,
132
+ readFileSync: (path) => {
133
+ assert.equal(path, `../../aviaratech-ai-delivery-${version}.tgz`);
134
+ return archive;
135
+ },
136
+ spawnSync: (command, args) => {
137
+ commands.push({ command, args: [...args] });
138
+ assert.equal(command, 'npm');
139
+ assert.equal(args[0], 'publish');
140
+ publications++;
141
+ return { status: fixture.publishStatus === undefined ? 0 : fixture.publishStatus };
142
+ },
143
+ pkg,
144
+ process: {
145
+ env: {
146
+ GITHUB_SHA: source,
147
+ REVIEWED_SOURCE_SHA: source,
148
+ RELEASE_VERSION: version,
149
+ REVIEWED_ARCHIVE_SHA256: digest,
150
+ GH_TOKEN: 'synthetic',
151
+ },
152
+ },
153
+ fetch,
154
+ AbortSignal: {
155
+ timeout: (ms) => {
156
+ timeouts.push(ms);
157
+ return new AbortController().signal;
158
+ },
159
+ },
160
+ URL,
161
+ Buffer,
162
+ performance: { now: () => elapsed },
163
+ console: {
164
+ log: (message) => {
165
+ messages.push(String(message));
166
+ },
167
+ },
168
+ setTimeout: (callback, ms) => {
169
+ waits.push(ms);
170
+ elapsed += ms;
171
+ queueMicrotask(callback);
172
+ return 0;
173
+ },
174
+ };
175
+ let error = null;
176
+ try {
177
+ await new Script(`(async () => {${program}\n})()`).runInNewContext(context);
178
+ }
179
+ catch (failure) {
180
+ error = String(failure);
181
+ }
182
+ return { error, publications, requests, commands, waits, timeouts, messages, elapsed, reads };
183
+ }
184
+ test('successful publication reconciles staggered registry visibility without another npm invocation', async () => {
185
+ const result = await runRelease({
186
+ versionReadyAt: 2,
187
+ archiveReadyAt: 3,
188
+ provenanceReadyAt: 4,
189
+ indexReadyAt: 5,
190
+ latestReadyAt: 6,
191
+ });
192
+ assert.equal(result.error, null);
193
+ assert.equal(result.publications, 1);
194
+ assert.equal(result.commands.length, 1);
195
+ assert.deepEqual(result.commands[0]?.args, [
196
+ 'publish',
197
+ '.',
198
+ '--ignore-scripts',
199
+ '--access',
200
+ 'public',
201
+ '--tag',
202
+ 'latest',
203
+ '--registry',
204
+ registry,
205
+ '--provenance',
206
+ ]);
207
+ assert.ok(result.waits.length > 0, 'Stale reads must be paced.');
208
+ assert.ok(result.waits.every((ms) => ms > 0));
209
+ assert.ok(result.requests.every((request) => request.method === 'GET'));
210
+ assert.ok(result.timeouts.every((ms) => ms > 0 && ms <= 30_000));
211
+ assert.ok(result.messages.some((message) => /exact reviewed registry/iu.test(message)));
212
+ });
213
+ test('matching occupied release reconciles latest without publication or retag', async () => {
214
+ const result = await runRelease({ occupied: true, latestReadyAt: 3 });
215
+ assert.equal(result.error, null);
216
+ assert.equal(result.publications, 0);
217
+ assert.deepEqual(result.commands, []);
218
+ assert.ok(result.messages.some((message) => /no publication repeated/iu.test(message)));
219
+ });
220
+ test('conflicting archive bytes fail immediately after one publication', async () => {
221
+ const result = await runRelease({ corruptArchive: true });
222
+ assert.match(result.error ?? '', /archive differs/iu);
223
+ assert.equal(result.publications, 1);
224
+ assert.deepEqual(result.waits, []);
225
+ });
226
+ test('unresolved visibility reports the external reconciliation bound without republishing', async () => {
227
+ const result = await runRelease({ versionReadyAt: Infinity });
228
+ assert.match(result.error ?? '', /reconciliation unresolved/iu);
229
+ assert.equal(result.publications, 1);
230
+ assert.ok(result.waits.length > 0);
231
+ assert.ok(result.elapsed < 10 * 60_000, 'Reconciliation must fit the existing external publish job.');
232
+ assert.ok(result.timeouts.some((ms) => ms < 30_000), 'Requests must respect the remaining reconciliation allowance.');
233
+ });
234
+ test('an occupied version visible only in the index never enters publication', async () => {
235
+ const result = await runRelease({ occupied: true, versionReadyAt: 3, latestReadyAt: 4 });
236
+ assert.equal(result.error, null);
237
+ assert.deepEqual(result.commands, []);
238
+ assert.ok(result.waits.length > 0);
239
+ });
240
+ for (const conflict of [
241
+ 'name',
242
+ 'version',
243
+ 'integrity',
244
+ 'provenance',
245
+ 'origin',
246
+ 'credentials',
247
+ 'index',
248
+ 'latest',
249
+ ]) {
250
+ test(`present ${conflict} conflict is refused without another publication`, async () => {
251
+ const result = await runRelease({ conflict });
252
+ assert.match(result.error ?? '', /differs|unexpected|advanced/iu);
253
+ assert.equal(result.publications, 1);
254
+ // The package index is initially absent; its conflict is observed on the second read.
255
+ if (conflict !== 'index')
256
+ assert.deepEqual(result.waits, []);
257
+ else
258
+ assert.equal(result.waits.length, 1);
259
+ });
260
+ }
261
+ for (const errorPhase of ['version', 'index', 'archive']) {
262
+ for (const errorStatus of [401, 403, 429, 503]) {
263
+ test(`${errorPhase} HTTP ${errorStatus} remains a permanent failure`, async () => {
264
+ const result = await runRelease({ errorPhase, errorStatus });
265
+ assert.match(result.error ?? '', new RegExp(`readback failed: ${errorStatus}`, 'u'));
266
+ assert.equal(result.publications, 1);
267
+ assert.deepEqual(result.waits, []);
268
+ });
269
+ }
270
+ }
271
+ test('network failure is not retried as propagation', async () => {
272
+ const result = await runRelease({ errorPhase: 'version', networkFailure: true });
273
+ assert.match(result.error ?? '', /synthetic network failure/u);
274
+ assert.equal(result.publications, 1);
275
+ assert.deepEqual(result.waits, []);
276
+ });
277
+ for (const publishStatus of [1, null]) {
278
+ test(`publish status ${publishStatus} remains ambiguous after matching readback`, async () => {
279
+ const result = await runRelease({ publishStatus, versionReadyAt: 3 });
280
+ assert.match(result.error ?? '', /failed or was ambiguous/iu);
281
+ assert.equal(result.publications, 1);
282
+ assert.ok(result.messages.some((message) => message.includes(`"status":${publishStatus}`)));
283
+ assert.ok(result.messages.some((message) => /exact reviewed registry/iu.test(message)));
284
+ });
285
+ }
286
+ test('an occupied conflicting release is never republished', async () => {
287
+ const result = await runRelease({ occupied: true, conflict: 'integrity' });
288
+ assert.match(result.error ?? '', /integrity differs/iu);
289
+ assert.deepEqual(result.commands, []);
290
+ });
291
+ test('protected source drift refuses publication', async () => {
292
+ const result = await runRelease({ conflict: 'main' });
293
+ assert.match(result.error ?? '', /protected main.*drifted/iu);
294
+ assert.deepEqual(result.commands, []);
295
+ });
296
+ test('oversized registry metadata fails within the readback output bound', async () => {
297
+ const result = await runRelease({ oversizedManifest: true });
298
+ assert.match(result.error ?? '', /readback bound/iu);
299
+ assert.equal(result.publications, 1);
300
+ assert.deepEqual(result.waits, []);
301
+ });
302
+ test('a body timeout at the reconciliation bound retains an explicit unresolved result', async () => {
303
+ const result = await runRelease({ bodyDeadlineExceeded: true });
304
+ assert.match(result.error ?? '', /reconciliation unresolved/iu);
305
+ assert.equal(result.publications, 1);
306
+ });
307
+ for (const phase of ['archiveReadyAt', 'provenanceReadyAt', 'indexReadyAt', 'latestReadyAt']) {
308
+ test(`unresolved ${phase} cannot be accepted as a completed release`, async () => {
309
+ const result = await runRelease({ [phase]: Infinity });
310
+ assert.match(result.error ?? '', /reconciliation unresolved/iu);
311
+ assert.equal(result.publications, 1);
312
+ });
313
+ }
314
+ //# sourceMappingURL=releaseReconciliation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"releaseReconciliation.test.js","sourceRoot":"","sources":["../src/releaseReconciliation.test.ts"],"names":[],"mappings":"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"}
package/dist/review.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { DeliveryConfig } from './config/deliveryConfig.js';
3
- import { type RepositoryApprovalBinding, type RepositoryClassificationReceipt } from './delivery/index.js';
3
+ import { type RepositoryApprovalBinding, type RepositoryClassificationReceipt } from './delivery/legacy.js';
4
4
  import type { DeliveryContext } from './issue.js';
5
5
  export declare const ReviewArtifactSchema: z.ZodObject<{
6
6
  artifactId: z.ZodString;
@@ -88,28 +88,58 @@ export declare function readRequiredReviewState(context: DeliveryContext, prNumb
88
88
  login: string;
89
89
  }): Promise<RequiredReviewState>;
90
90
  export declare function parseReviewArtifact(raw: unknown): ReviewArtifact;
91
+ /** Preserve the original binding: GitHub can remap a review's commit_id after a rebase. */
92
+ export declare function parseGitHubReviewArtifact(body: string | null | undefined): ReviewArtifact;
93
+ export declare function readGitHubPrScopeHash(context: DeliveryContext, prNumber: number, changedFiles: number): Promise<string>;
91
94
  export declare function reviewArtifactApproval(input: {
92
95
  artifact: ReviewArtifact;
93
96
  classification: RepositoryClassificationReceipt;
94
- config: DeliveryConfig;
97
+ config: Pick<DeliveryConfig, 'roles'>;
95
98
  issueNumber: number;
96
99
  prNumber?: number;
97
100
  }): RepositoryApprovalBinding;
98
101
  export declare function savePrepublicationArtifact(input: {
99
102
  artifact: ReviewArtifact;
100
103
  classification: RepositoryClassificationReceipt;
101
- config: DeliveryConfig;
104
+ config: Pick<DeliveryConfig, 'roles'>;
102
105
  issueNumber: number;
103
106
  repoRoot: string;
104
107
  }): void;
105
108
  export declare function loadPrepublicationArtifact(repoRoot: string, issueNumber: number, headSha: string): ReviewArtifact | null;
106
- export declare function submitReview(input: {
109
+ interface ReviewSubmissionInput {
107
110
  artifact: ReviewArtifact;
108
111
  context: DeliveryContext;
109
112
  issueNumber: number;
110
113
  prNumber: number;
111
- publicationEvidenceId: string;
112
- classification: RepositoryClassificationReceipt;
113
- }): Promise<SubmittedReviewReceipt>;
114
+ dryRun?: boolean;
115
+ publicationEvidenceId?: string;
116
+ classification?: RepositoryClassificationReceipt;
117
+ }
118
+ export interface GitHubReviewReceipt {
119
+ artifact: ReviewArtifact;
120
+ repository: string;
121
+ baseSha: string;
122
+ headSha: string;
123
+ githubReviewId: number;
124
+ githubReviewUrl: string;
125
+ login: string;
126
+ prNumber: number;
127
+ schemaVersion: 'ai-delivery.github-review@1';
128
+ state: 'APPROVED';
129
+ receiptId: string;
130
+ }
131
+ export interface ReviewDryRun {
132
+ dryRun: true;
133
+ artifactId: string;
134
+ headSha: string;
135
+ }
136
+ export declare function submitReview(input: ReviewSubmissionInput & {
137
+ dryRun: true;
138
+ }): Promise<ReviewDryRun>;
139
+ export declare function submitReview(input: ReviewSubmissionInput & {
140
+ dryRun?: false;
141
+ }): Promise<GitHubReviewReceipt>;
142
+ export declare function submitReview(input: ReviewSubmissionInput): Promise<GitHubReviewReceipt | ReviewDryRun>;
114
143
  export declare function loadSubmittedReview(repoRoot: string, issueNumber: number, headSha: string): SubmittedReviewReceipt | null;
115
144
  export declare function assertSubmittedReviewCurrent(context: DeliveryContext, receipt: SubmittedReviewReceipt): Promise<void>;
145
+ export {};
package/dist/review.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { join } from 'node:path';
2
2
  import { z } from 'zod';
3
3
  import { assertPrivateFile } from './delivery/common.js';
4
- import { digestValue, RepositoryApprovalBindingSchema, } from './delivery/index.js';
4
+ import { digestValue, RepositoryApprovalBindingSchema, } from './delivery/legacy.js';
5
5
  import { DeliveryError } from './errors.js';
6
6
  import { gitCommonDir } from './git.js';
7
7
  import { writePrivateJsonFileAtomically } from './utils/atomicJson.js';
@@ -144,6 +144,50 @@ export function parseReviewArtifact(raw) {
144
144
  const value = typeof raw === 'string' ? JSON.parse(raw) : raw;
145
145
  return ReviewArtifactSchema.parse(value);
146
146
  }
147
+ /** Preserve the original binding: GitHub can remap a review's commit_id after a rebase. */
148
+ export function parseGitHubReviewArtifact(body) {
149
+ try {
150
+ if (typeof body !== 'string' || Buffer.byteLength(body, 'utf8') > 64 * 1024)
151
+ throw new Error('Invalid size.');
152
+ const markers = body.match(/<!-- ai-delivery-review-artifact:[\s\S]*?-->/gu) ?? [];
153
+ const payloads = body.match(/<!-- ai-delivery-review-artifact-data:[\s\S]*?-->/gu) ?? [];
154
+ if (markers.length !== 1 || payloads.length !== 1)
155
+ throw new Error('Ambiguous binding.');
156
+ const id = markers[0].match(/^<!-- ai-delivery-review-artifact: (sha256:[a-f0-9]{64}) -->$/u)?.[1];
157
+ const encoded = payloads[0].match(/^<!-- ai-delivery-review-artifact-data: ([A-Za-z0-9_-]+) -->$/u)?.[1];
158
+ if (!id || !encoded)
159
+ throw new Error('Invalid binding.');
160
+ const bytes = Buffer.from(encoded, 'base64url');
161
+ const json = bytes.toString('utf8');
162
+ if (bytes.toString('base64url') !== encoded || !Buffer.from(json, 'utf8').equals(bytes))
163
+ throw new Error('Invalid encoding.');
164
+ const artifact = parseReviewArtifact(json);
165
+ if (artifact.artifactId !== id)
166
+ throw new Error('Artifact identity differs.');
167
+ return artifact;
168
+ }
169
+ catch {
170
+ throw new DeliveryError('GitHub review artifact binding is missing, invalid or ambiguous; obtain a fresh review.');
171
+ }
172
+ }
173
+ function githubReviewBody(artifact) {
174
+ ReviewArtifactSchema.parse(artifact);
175
+ const body = `${artifact.summary}\n\n<!-- ai-delivery-review-artifact: ${artifact.artifactId} -->\n<!-- ai-delivery-review-artifact-data: ${Buffer.from(JSON.stringify(artifact)).toString('base64url')} -->`;
176
+ parseGitHubReviewArtifact(body);
177
+ return body;
178
+ }
179
+ export async function readGitHubPrScopeHash(context, prNumber, changedFiles) {
180
+ const files = [];
181
+ for (let page = 1; page <= 30; page++) {
182
+ const batch = (await context.clients.rest.pulls.listFiles({ ...context.repo, pull_number: prNumber, page, per_page: 100 })).data;
183
+ files.push(...batch.map((file) => file.filename));
184
+ if (batch.length < 100)
185
+ break;
186
+ }
187
+ if (!Number.isSafeInteger(changedFiles) || files.length !== changedFiles || new Set(files).size !== files.length)
188
+ throw new DeliveryError('Independent review scope is incomplete or differs from the remote PR diff.');
189
+ return digestValue(files.sort());
190
+ }
147
191
  export function reviewArtifactApproval(input) {
148
192
  const { artifact, classification, config } = input;
149
193
  ReviewArtifactSchema.parse(artifact);
@@ -207,76 +251,93 @@ async function listAllReviews(context, prNumber) {
207
251
  }
208
252
  export async function submitReview(input) {
209
253
  const { artifact, context, issueNumber, prNumber } = input;
210
- reviewArtifactApproval({
211
- artifact,
212
- classification: input.classification,
213
- config: context.config,
214
- issueNumber,
215
- prNumber,
216
- });
217
- if (context.clients.authSource !== 'app' || context.clients.role !== 'reviewer' || !context.clients.appActorLogin) {
218
- throw new DeliveryError('Formal review requires the reviewer GitHub App role.');
219
- }
254
+ ReviewArtifactSchema.parse(artifact);
255
+ if (artifact.verdict !== 'approve' ||
256
+ artifact.issueNumber !== issueNumber ||
257
+ artifact.authorIdentity !== context.config.roles.author.identity ||
258
+ artifact.reviewerIdentity !== context.config.roles.reviewer.identity ||
259
+ (artifact.prNumber !== null && artifact.prNumber !== prNumber))
260
+ throw new DeliveryError('Independent review artifact is foreign or rejected.');
261
+ if (context.clients.authSource !== 'app' || context.clients.role !== 'reviewer' || !context.clients.appActorLogin)
262
+ throw new DeliveryError('Formal review requires the independent reviewer GitHub App role.');
220
263
  const pr = (await context.clients.rest.pulls.get({ ...context.repo, pull_number: prNumber })).data;
221
- if (pr.head.sha !== artifact.head.sha || pr.state !== 'open')
222
- throw new DeliveryError('PR head changed before formal review.');
264
+ if (pr.number !== prNumber ||
265
+ pr.head.sha !== artifact.head.sha ||
266
+ pr.state !== 'open' ||
267
+ pr.base.repo?.full_name.toLowerCase() !== context.config.repository.toLowerCase() ||
268
+ pr.head.repo?.full_name.toLowerCase() !== context.config.repository.toLowerCase())
269
+ throw new DeliveryError('PR identity or head changed before formal review.');
223
270
  const actor = await context.clients.appActorLogin();
224
- if (!actor || !pr.user?.login || actor.toLowerCase() === pr.user.login.toLowerCase()) {
271
+ if (!actor || !pr.user?.login || actor.toLowerCase() === pr.user.login.toLowerCase())
225
272
  throw new DeliveryError('Reviewer GitHub actor must differ from PR author.');
226
- }
227
- const existing = loadSubmittedReview(context.root, issueNumber, artifact.head.sha);
228
- if (existing) {
229
- if (existing.artifact.artifactId !== artifact.artifactId ||
230
- existing.prNumber !== prNumber ||
231
- existing.publicationEvidenceId !== input.publicationEvidenceId ||
232
- existing.login.toLowerCase() !== actor.toLowerCase()) {
233
- throw new DeliveryError('An exact-head review receipt already binds different review evidence.');
234
- }
235
- await assertSubmittedReviewCurrent(context, existing);
236
- return existing;
237
- }
273
+ const commit = (await context.clients.rest.git.getCommit({ ...context.repo, commit_sha: artifact.head.sha })).data;
274
+ if (commit.sha !== artifact.head.sha || commit.tree.sha !== artifact.head.tree)
275
+ throw new DeliveryError('Independent review tree differs from the remote commit.');
276
+ if (artifact.diffScopeHash !== (await readGitHubPrScopeHash(context, prNumber, pr.changed_files)))
277
+ throw new DeliveryError('Independent review scope is incomplete or differs from the remote PR diff.');
278
+ const body = githubReviewBody(artifact);
238
279
  const marker = `<!-- ai-delivery-review-artifact: ${artifact.artifactId} -->`;
239
- const matches = (await listAllReviews(context, prNumber)).filter((review) => review.body?.includes(marker));
280
+ const history = await listAllReviews(context, prNumber);
281
+ if (history.some((review) => review.user?.login.toLowerCase() === actor.toLowerCase() &&
282
+ review.commit_id === artifact.head.sha &&
283
+ review.state === 'APPROVED' &&
284
+ /<!-- ai-delivery-review-artifact: sha256:[a-f0-9]{64} -->/u.test(review.body ?? '') &&
285
+ !review.body?.includes(marker)))
286
+ throw new DeliveryError('Another independent artifact conflicts with the existing exact-head App approval.');
287
+ const matches = history.filter((review) => review.body?.includes(marker));
240
288
  if (matches.length > 1)
241
- throw new DeliveryError('Multiple GitHub reviews claim the same artifact identity.');
289
+ throw new DeliveryError('Conflicting GitHub reviews claim the same artifact identity.');
242
290
  const prior = matches[0];
243
291
  if (prior &&
244
292
  (prior.state !== 'APPROVED' ||
245
293
  prior.commit_id !== artifact.head.sha ||
246
- prior.user?.login?.toLowerCase() !== actor.toLowerCase() ||
247
- !prior.html_url)) {
248
- throw new DeliveryError('Prior GitHub review marker does not match exact approval.');
249
- }
294
+ prior.user?.login.toLowerCase() !== actor.toLowerCase() ||
295
+ !prior.html_url ||
296
+ parseGitHubReviewArtifact(prior.body).artifactId !== artifact.artifactId))
297
+ throw new DeliveryError('Prior GitHub review marker conflicts with exact-head approval.');
298
+ const fresh = (await context.clients.rest.pulls.get({ ...context.repo, pull_number: prNumber })).data;
299
+ if (fresh.head.sha !== artifact.head.sha || fresh.base.sha !== pr.base.sha || fresh.state !== 'open')
300
+ throw new DeliveryError('PR moved during independent review validation.');
301
+ if (input.dryRun)
302
+ return { dryRun: true, artifactId: artifact.artifactId, headSha: artifact.head.sha };
250
303
  const submitted = prior ??
251
304
  (await context.clients.rest.pulls.createReview({
252
305
  ...context.repo,
253
306
  pull_number: prNumber,
254
307
  commit_id: artifact.head.sha,
255
- body: `${artifact.summary}\n\n${marker}`,
308
+ body,
256
309
  event: 'APPROVE',
257
310
  })).data;
258
311
  if (!submitted.id ||
259
312
  submitted.state !== 'APPROVED' ||
260
313
  submitted.commit_id !== artifact.head.sha ||
261
- submitted.user?.login?.toLowerCase() !== actor.toLowerCase() ||
262
- !submitted.html_url) {
263
- throw new DeliveryError('GitHub did not return complete exact-head approval readback.');
264
- }
314
+ submitted.user?.login.toLowerCase() !== actor.toLowerCase() ||
315
+ !submitted.html_url)
316
+ throw new DeliveryError('GitHub did not return complete exact-head independent approval.');
265
317
  const content = {
266
318
  artifact,
267
- completedAt: new Date().toISOString(),
319
+ repository: context.config.repository,
320
+ baseSha: pr.base.sha,
321
+ headSha: artifact.head.sha,
268
322
  githubReviewId: submitted.id,
269
323
  githubReviewUrl: submitted.html_url,
270
- headSha: artifact.head.sha,
271
324
  login: submitted.user.login,
272
325
  prNumber,
273
- publicationEvidenceId: input.publicationEvidenceId,
274
- schemaVersion: 'ai-delivery.review-receipt@1',
326
+ schemaVersion: 'ai-delivery.github-review@1',
275
327
  state: 'APPROVED',
276
328
  };
277
- const receipt = SubmittedReviewReceiptSchema.parse({ ...content, receiptId: digestValue(content) });
278
- await assertSubmittedReviewCurrent(context, receipt);
279
- writePrivateJsonFileAtomically(receiptPath(context.root, issueNumber, artifact.head.sha), receipt);
329
+ const receipt = { ...content, receiptId: digestValue(content) };
330
+ const readback = (await context.clients.rest.pulls.getReview({ ...context.repo, pull_number: prNumber, review_id: submitted.id })).data;
331
+ if (readback.id !== receipt.githubReviewId ||
332
+ readback.html_url !== receipt.githubReviewUrl ||
333
+ readback.state !== 'APPROVED' ||
334
+ readback.commit_id !== receipt.headSha ||
335
+ readback.user?.login !== receipt.login ||
336
+ parseGitHubReviewArtifact(readback.body).artifactId !== artifact.artifactId)
337
+ throw new DeliveryError('GitHub exact-head App review readback disagrees.');
338
+ const current = (await context.clients.rest.pulls.get({ ...context.repo, pull_number: prNumber })).data;
339
+ if (current.head.sha !== artifact.head.sha || current.base.sha !== pr.base.sha || current.state !== 'open')
340
+ throw new DeliveryError('PR moved during independent review submission; approval is not current.');
280
341
  return receipt;
281
342
  }
282
343
  export function loadSubmittedReview(repoRoot, issueNumber, headSha) {
@@ -299,7 +360,7 @@ export async function assertSubmittedReviewCurrent(context, receipt) {
299
360
  review.commit_id !== receipt.headSha ||
300
361
  review.user?.login !== receipt.login ||
301
362
  review.html_url !== receipt.githubReviewUrl ||
302
- !review.body?.includes(`<!-- ai-delivery-review-artifact: ${receipt.artifact.artifactId} -->`)) {
363
+ parseGitHubReviewArtifact(review.body).artifactId !== receipt.artifact.artifactId) {
303
364
  throw new DeliveryError('GitHub exact-head review no longer matches the stored receipt.');
304
365
  }
305
366
  }