@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.
- package/CONTRIBUTING.md +9 -0
- package/README.md +77 -442
- package/dist/agent.d.ts +5 -11
- package/dist/agent.js +4 -8
- package/dist/agent.js.map +1 -1
- package/dist/cli.js +59 -107
- package/dist/cli.js.map +1 -1
- package/dist/config/deliveryConfig.d.ts +44 -55
- package/dist/config/deliveryConfig.js +85 -88
- package/dist/config/deliveryConfig.js.map +1 -1
- package/dist/config/deliveryConfig.test.js +13 -5
- package/dist/config/deliveryConfig.test.js.map +1 -1
- package/dist/delivery/common.d.ts +4 -1
- package/dist/delivery/common.js +56 -8
- package/dist/delivery/common.js.map +1 -1
- package/dist/delivery/delivery.test.js +2 -3
- package/dist/delivery/delivery.test.js.map +1 -1
- package/dist/delivery/index.d.ts +0 -6
- package/dist/delivery/index.js +0 -3
- package/dist/delivery/index.js.map +1 -1
- package/dist/delivery/legacy.d.ts +8 -0
- package/dist/delivery/legacy.js +5 -0
- package/dist/delivery/legacy.js.map +1 -0
- package/dist/delivery/policy.js +2 -13
- package/dist/delivery/policy.js.map +1 -1
- package/dist/delivery/stage.d.ts +6 -0
- package/dist/delivery/stage.js +11 -2
- package/dist/delivery/stage.js.map +1 -1
- package/dist/directoryIndependent.test.d.ts +1 -0
- package/dist/directoryIndependent.test.js +262 -0
- package/dist/directoryIndependent.test.js.map +1 -0
- package/dist/dispatch.d.ts +29 -3
- package/dist/dispatch.js +160 -457
- package/dist/dispatch.js.map +1 -1
- package/dist/genericCompatibility.test.js +0 -35
- package/dist/genericCompatibility.test.js.map +1 -1
- package/dist/git.js +10 -3
- package/dist/git.js.map +1 -1
- package/dist/gitProcess.d.ts +6 -0
- package/dist/gitProcess.js +56 -0
- package/dist/gitProcess.js.map +1 -0
- package/dist/github/discovery.d.ts +1 -0
- package/dist/github/discovery.js +1 -1
- package/dist/github/discovery.js.map +1 -1
- package/dist/github/discovery.test.js +25 -20
- package/dist/github/discovery.test.js.map +1 -1
- package/dist/github/nativeIssueMetadata.d.ts +2 -2
- package/dist/github/nativeIssueMetadata.js.map +1 -1
- package/dist/github/projectDelivery.d.ts +1 -1
- package/dist/github/projectDelivery.js.map +1 -1
- package/dist/github/repo.d.ts +7 -1
- package/dist/github/repo.js +58 -2
- package/dist/github/repo.js.map +1 -1
- package/dist/issue.d.ts +11 -6
- package/dist/issue.js +136 -319
- package/dist/issue.js.map +1 -1
- package/dist/issueJournal.d.ts +1 -1
- package/dist/lifecycle.test.js +40 -7371
- package/dist/lifecycle.test.js.map +1 -1
- package/dist/logger.js +4 -4
- package/dist/logger.js.map +1 -1
- package/dist/mcp/index.js +2 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/tools.d.ts +27 -42
- package/dist/mcp/tools.js +34 -42
- package/dist/mcp/tools.js.map +1 -1
- package/dist/pluginInstaller.d.ts +51 -0
- package/dist/pluginInstaller.js +774 -0
- package/dist/pluginInstaller.js.map +1 -0
- package/dist/pluginInstaller.test.d.ts +1 -0
- package/dist/pluginInstaller.test.js +543 -0
- package/dist/pluginInstaller.test.js.map +1 -0
- package/dist/pluginPackaging.test.d.ts +1 -0
- package/dist/pluginPackaging.test.js +245 -0
- package/dist/pluginPackaging.test.js.map +1 -0
- package/dist/pr.d.ts +66 -114
- package/dist/pr.js +288 -1108
- package/dist/pr.js.map +1 -1
- package/dist/pr.test.js +295 -237
- package/dist/pr.test.js.map +1 -1
- package/dist/releaseReconciliation.test.d.ts +1 -0
- package/dist/releaseReconciliation.test.js +314 -0
- package/dist/releaseReconciliation.test.js.map +1 -0
- package/dist/review.d.ts +37 -7
- package/dist/review.js +106 -45
- package/dist/review.js.map +1 -1
- package/dist/review.test.js +30 -71
- package/dist/review.test.js.map +1 -1
- package/dist/services/agentReadinessService.d.ts +1 -1
- package/dist/services/agentReadinessService.js +2 -0
- package/dist/services/agentReadinessService.js.map +1 -1
- package/dist/services/deliveryAdmission.d.ts +4 -4
- package/dist/services/deliveryAdmission.js.map +1 -1
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +71 -10
- package/dist/setup.js.map +1 -1
- package/dist/setup.test.js +594 -204
- package/dist/setup.test.js.map +1 -1
- package/dist/start.test.d.ts +1 -0
- package/dist/start.test.js +72 -0
- package/dist/start.test.js.map +1 -0
- package/dist/stdoutRegression.test.d.ts +1 -0
- package/dist/stdoutRegression.test.js +326 -0
- package/dist/stdoutRegression.test.js.map +1 -0
- package/dist/verification.d.ts +62 -27
- package/dist/verification.js +172 -1337
- package/dist/verification.js.map +1 -1
- package/dist/worktree.d.ts +1 -31
- package/dist/worktree.js +5 -200
- package/dist/worktree.js.map +1 -1
- package/dist/worktreeTransition.d.ts +1 -1
- package/dist/worktreeTransition.js +10 -5
- package/dist/worktreeTransition.js.map +1 -1
- package/dist/worktreeTransition.test.js +2 -3
- package/dist/worktreeTransition.test.js.map +1 -1
- package/package.json +4 -4
- package/plugins/ai-delivery/.claude-plugin/plugin.json +6 -1
- package/plugins/ai-delivery/LICENSE +21 -0
- package/plugins/ai-delivery/README.md +4 -2
- package/plugins/ai-delivery/dist/mcp-launcher.js +19 -8
- package/plugins/ai-delivery/mcp.json +10 -0
- package/plugins/ai-delivery/plugin.json +10 -0
- package/plugins/ai-delivery/runtime/dist/THIRD-PARTY-NOTICES.md +1010 -0
- package/plugins/ai-delivery/runtime/dist/cli.js +276 -0
- package/plugins/ai-delivery/runtime/package.json +13 -0
- package/plugins/ai-delivery/skills/intake-create/SKILL.md +1 -1
- package/plugins/ai-delivery/skills/pr-handoff/SKILL.md +14 -21
- package/plugins/ai-delivery/skills/worktree-lifecycle/SKILL.md +12 -23
- package/dist/services/deliveryRecordService.d.ts +0 -47
- package/dist/services/deliveryRecordService.js +0 -199
- 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/
|
|
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
|
-
|
|
109
|
+
interface ReviewSubmissionInput {
|
|
107
110
|
artifact: ReviewArtifact;
|
|
108
111
|
context: DeliveryContext;
|
|
109
112
|
issueNumber: number;
|
|
110
113
|
prNumber: number;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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/
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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.
|
|
222
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
|
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('
|
|
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
|
|
247
|
-
!prior.html_url
|
|
248
|
-
|
|
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
|
|
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
|
|
262
|
-
!submitted.html_url)
|
|
263
|
-
throw new DeliveryError('GitHub did not return complete exact-head approval
|
|
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
|
-
|
|
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
|
-
|
|
274
|
-
schemaVersion: 'ai-delivery.review-receipt@1',
|
|
326
|
+
schemaVersion: 'ai-delivery.github-review@1',
|
|
275
327
|
state: 'APPROVED',
|
|
276
328
|
};
|
|
277
|
-
const receipt =
|
|
278
|
-
await
|
|
279
|
-
|
|
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
|
-
|
|
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
|
}
|