@aviaratech/ai-delivery 0.3.23 → 0.3.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CONTRIBUTING.md +166 -16
  2. package/README.md +75 -446
  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/agentReadiness.test.d.ts +1 -0
  7. package/dist/agentReadiness.test.js +139 -0
  8. package/dist/agentReadiness.test.js.map +1 -0
  9. package/dist/cli.js +27 -111
  10. package/dist/cli.js.map +1 -1
  11. package/dist/config/deliveryConfig.d.ts +44 -55
  12. package/dist/config/deliveryConfig.js +85 -88
  13. package/dist/config/deliveryConfig.js.map +1 -1
  14. package/dist/config/deliveryConfig.test.js +13 -5
  15. package/dist/config/deliveryConfig.test.js.map +1 -1
  16. package/dist/contributorChecks.test.d.ts +1 -0
  17. package/dist/contributorChecks.test.js +1010 -0
  18. package/dist/contributorChecks.test.js.map +1 -0
  19. package/dist/currentConsumer.test.d.ts +1 -0
  20. package/dist/currentConsumer.test.js +832 -0
  21. package/dist/currentConsumer.test.js.map +1 -0
  22. package/dist/delivery/common.js +7 -2
  23. package/dist/delivery/common.js.map +1 -1
  24. package/dist/delivery/delivery.test.js +2 -3
  25. package/dist/delivery/delivery.test.js.map +1 -1
  26. package/dist/delivery/index.d.ts +0 -6
  27. package/dist/delivery/index.js +0 -3
  28. package/dist/delivery/index.js.map +1 -1
  29. package/dist/delivery/legacy.d.ts +8 -0
  30. package/dist/delivery/legacy.js +5 -0
  31. package/dist/delivery/legacy.js.map +1 -0
  32. package/dist/delivery/policy.js +2 -13
  33. package/dist/delivery/policy.js.map +1 -1
  34. package/dist/dependencyCompatibility.test.d.ts +1 -0
  35. package/dist/dependencyCompatibility.test.js +175 -0
  36. package/dist/dependencyCompatibility.test.js.map +1 -0
  37. package/dist/directoryIndependent.test.d.ts +1 -0
  38. package/dist/directoryIndependent.test.js +352 -0
  39. package/dist/directoryIndependent.test.js.map +1 -0
  40. package/dist/dispatch.d.ts +29 -3
  41. package/dist/dispatch.js +166 -457
  42. package/dist/dispatch.js.map +1 -1
  43. package/dist/genericCompatibility.test.js +0 -35
  44. package/dist/genericCompatibility.test.js.map +1 -1
  45. package/dist/git.js +10 -3
  46. package/dist/git.js.map +1 -1
  47. package/dist/gitProcess.d.ts +6 -0
  48. package/dist/gitProcess.js +56 -0
  49. package/dist/gitProcess.js.map +1 -0
  50. package/dist/github/discovery.d.ts +1 -0
  51. package/dist/github/discovery.js +1 -1
  52. package/dist/github/discovery.js.map +1 -1
  53. package/dist/github/discovery.test.js +25 -20
  54. package/dist/github/discovery.test.js.map +1 -1
  55. package/dist/github/nativeIssueMetadata.d.ts +2 -2
  56. package/dist/github/nativeIssueMetadata.js.map +1 -1
  57. package/dist/github/projectDelivery.d.ts +1 -1
  58. package/dist/github/projectDelivery.js.map +1 -1
  59. package/dist/github/repo.d.ts +7 -1
  60. package/dist/github/repo.js +58 -2
  61. package/dist/github/repo.js.map +1 -1
  62. package/dist/issue.d.ts +18 -6
  63. package/dist/issue.js +136 -319
  64. package/dist/issue.js.map +1 -1
  65. package/dist/issueJournal.d.ts +1 -1
  66. package/dist/issueJournal.test.js +88 -6
  67. package/dist/issueJournal.test.js.map +1 -1
  68. package/dist/lifecycle.test.js +40 -7371
  69. package/dist/lifecycle.test.js.map +1 -1
  70. package/dist/logger.js +4 -4
  71. package/dist/logger.js.map +1 -1
  72. package/dist/mcp/tools.d.ts +32 -42
  73. package/dist/mcp/tools.js +51 -42
  74. package/dist/mcp/tools.js.map +1 -1
  75. package/dist/pluginInstaller.d.ts +1 -1
  76. package/dist/pluginInstaller.js +8 -2
  77. package/dist/pluginInstaller.js.map +1 -1
  78. package/dist/pluginInstaller.test.js +213 -2
  79. package/dist/pluginInstaller.test.js.map +1 -1
  80. package/dist/pluginPackaging.test.js +225 -2
  81. package/dist/pluginPackaging.test.js.map +1 -1
  82. package/dist/pr.d.ts +73 -114
  83. package/dist/pr.js +837 -1085
  84. package/dist/pr.js.map +1 -1
  85. package/dist/pr.test.js +453 -236
  86. package/dist/pr.test.js.map +1 -1
  87. package/dist/prAssociation.test.d.ts +1 -0
  88. package/dist/prAssociation.test.js +1336 -0
  89. package/dist/prAssociation.test.js.map +1 -0
  90. package/dist/prDispatch.test.d.ts +1 -0
  91. package/dist/prDispatch.test.js +96 -0
  92. package/dist/prDispatch.test.js.map +1 -0
  93. package/dist/releaseReconciliation.test.d.ts +1 -0
  94. package/dist/releaseReconciliation.test.js +318 -0
  95. package/dist/releaseReconciliation.test.js.map +1 -0
  96. package/dist/releaseWorkflow.test.d.ts +1 -0
  97. package/dist/releaseWorkflow.test.js +447 -0
  98. package/dist/releaseWorkflow.test.js.map +1 -0
  99. package/dist/review.d.ts +37 -7
  100. package/dist/review.js +106 -45
  101. package/dist/review.js.map +1 -1
  102. package/dist/review.test.js +30 -71
  103. package/dist/review.test.js.map +1 -1
  104. package/dist/services/agentReadinessService.d.ts +1 -1
  105. package/dist/services/agentReadinessService.js +4 -1
  106. package/dist/services/agentReadinessService.js.map +1 -1
  107. package/dist/services/deliveryAdmission.d.ts +4 -4
  108. package/dist/services/deliveryAdmission.js.map +1 -1
  109. package/dist/setup.test.js +10 -5
  110. package/dist/setup.test.js.map +1 -1
  111. package/dist/stagedChecks.test.d.ts +1 -0
  112. package/dist/stagedChecks.test.js +228 -0
  113. package/dist/stagedChecks.test.js.map +1 -0
  114. package/dist/start.test.d.ts +1 -0
  115. package/dist/start.test.js +72 -0
  116. package/dist/start.test.js.map +1 -0
  117. package/dist/stdoutRegression.test.d.ts +1 -0
  118. package/dist/stdoutRegression.test.js +326 -0
  119. package/dist/stdoutRegression.test.js.map +1 -0
  120. package/dist/verification.d.ts +1 -27
  121. package/dist/verification.js +5 -1332
  122. package/dist/verification.js.map +1 -1
  123. package/dist/worktree.d.ts +1 -31
  124. package/dist/worktree.js +5 -200
  125. package/dist/worktree.js.map +1 -1
  126. package/dist/worktreeTransition.d.ts +1 -1
  127. package/dist/worktreeTransition.js +10 -5
  128. package/dist/worktreeTransition.js.map +1 -1
  129. package/dist/worktreeTransition.test.js +2 -3
  130. package/dist/worktreeTransition.test.js.map +1 -1
  131. package/package.json +9 -6
  132. package/plugins/ai-delivery/.claude-plugin/plugin.json +2 -2
  133. package/plugins/ai-delivery/README.md +3 -3
  134. package/plugins/ai-delivery/plugin.json +1 -1
  135. package/plugins/ai-delivery/runtime/dist/THIRD-PARTY-NOTICES.md +1 -1
  136. package/plugins/ai-delivery/runtime/dist/cli.js +50 -59
  137. package/plugins/ai-delivery/runtime/package.json +1 -1
  138. package/plugins/ai-delivery/skills/intake-create/SKILL.md +1 -1
  139. package/plugins/ai-delivery/skills/pr-handoff/SKILL.md +14 -21
  140. package/plugins/ai-delivery/skills/worktree-lifecycle/SKILL.md +12 -23
  141. package/dist/services/deliveryRecordService.d.ts +0 -47
  142. package/dist/services/deliveryRecordService.js +0 -199
  143. package/dist/services/deliveryRecordService.js.map +0 -1
@@ -0,0 +1,832 @@
1
+ import assert from 'node:assert/strict';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { gzipSync } from 'node:zlib';
4
+ import { appendFileSync, chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { dirname, join } from 'node:path';
7
+ import { fileURLToPath, pathToFileURL } from 'node:url';
8
+ import { setTimeout as delay } from 'node:timers/promises';
9
+ import { test } from 'vitest';
10
+ const source = process.env['AI_DELIVERY_CURRENT_CONSUMER_SCRIPT'] ??
11
+ fileURLToPath(new URL('../scripts/current-consumer.mjs', import.meta.url));
12
+ const api = (await import(pathToFileURL(source).href));
13
+ // Optional caller-owned local evidence. Child environments still use the helper's
14
+ // explicit allowlist and never inherit this path or ambient credentials.
15
+ const evidencePath = process.env['AI_DELIVERY_CURRENT_CONSUMER_EVIDENCE_FILE'];
16
+ function recordSynthetic(evidence) {
17
+ if (evidencePath)
18
+ appendFileSync(evidencePath, JSON.stringify({ boundary: 'fictional synthetic fixture', evidence }) + '\n');
19
+ }
20
+ const helper = {
21
+ ...api,
22
+ runOwnedProcess: async (command, args, options) => {
23
+ try {
24
+ const result = await api.runOwnedProcess(command, args, options);
25
+ recordSynthetic({ kind: 'process-result', stdoutSha256: api.sha256(result.stdout), ...result.evidence });
26
+ return result;
27
+ }
28
+ catch (error) {
29
+ if (error instanceof Error) {
30
+ const e = error;
31
+ recordSynthetic({ kind: 'process-failure', code: e.code, evidence: e.evidence });
32
+ }
33
+ throw error;
34
+ }
35
+ },
36
+ };
37
+ const failure = (code) => (error) => error instanceof Error && error.code === code;
38
+ test.each([false, true])('synthetic actual Node24 coverage parent preserves reused explicit env; frozen=%s', (frozen) => {
39
+ const f = fixture();
40
+ let confirmedQuiescence = false;
41
+ try {
42
+ const coverage = join(f.directory, 'coverage');
43
+ mkdirSync(coverage);
44
+ const program = `import {runOwnedProcess,consumerEnvironment,sha256} from ${JSON.stringify(pathToFileURL(source).href)};
45
+ import process from 'node:process';
46
+ const input=consumerEnvironment(${JSON.stringify(f.directory)},process.execPath);
47
+ const env=${frozen ? 'Object.freeze(input)' : 'input'};
48
+ const before={...env};const keys=Object.keys(env).sort();
49
+ const report={boundary:'fictional synthetic process, actual Node24 coverage parent',actualRuntime:process.version,
50
+ coverageParentEnabled:typeof process.env.NODE_V8_COVERAGE==='string',frozenCaller:Object.isFrozen(env),
51
+ environmentValuesSerialized:false,phases:[],rejectedKeys:[],unresolvedOwnedChild:false,status:'failed'};
52
+ try{
53
+ if(process.version!=='v24.21.0'||!report.coverageParentEnabled)throw new Error('runtime');
54
+ for(let i=0;i<2;i++){
55
+ let spawned=false,result;
56
+ try{result=await runOwnedProcess(process.execPath,['-e','process.stdout.write("fictional child")'],{cwd:${JSON.stringify(f.directory)},env,onSpawn:()=>{spawned=true;}});}
57
+ catch(error){report.unresolvedOwnedChild=spawned&&error?.evidence?.quiescent!==true;throw error;}
58
+ if(result.stdout!=='fictional child'||!result.evidence.quiescent)throw new Error('proof');
59
+ report.phases.push({...result.evidence,stdoutSha256:sha256(result.stdout)});
60
+ }
61
+ for(const key of ['NODE_V8_COVERAGE','GH_TOKEN','GH_APP_ID_GPT_REVIEWER','AI_DELIVERY_CONFIG','NODE_PATH']){
62
+ let spawned=false,rejected=false;
63
+ try{await runOwnedProcess(process.execPath,['-e','process.exit(0)'],{cwd:${JSON.stringify(f.directory)},
64
+ env:{...env,[key]:'fictional unapproved value'},onSpawn:()=>{spawned=true;}});}
65
+ catch(error){rejected=error?.code==='environment';}
66
+ if(!rejected||spawned)throw new Error('exclusion');
67
+ report.rejectedKeys.push(key);
68
+ }
69
+ report.status='passed';
70
+ }catch(error){report.failureCode=typeof error?.code==='string'?error.code:'process';}
71
+ report.callerKeysUnchanged=JSON.stringify(Object.keys(env).sort())===JSON.stringify(keys);
72
+ report.callerValuesUnchanged=keys.every(key=>env[key]===before[key]);
73
+ console.log(JSON.stringify(report));`;
74
+ const parent = spawnSync(process.execPath, ['--input-type=module', '-e', program], {
75
+ cwd: f.directory,
76
+ env: {
77
+ PATH: dirname(process.execPath) + ':/usr/bin:/bin',
78
+ HOME: f.directory,
79
+ TMPDIR: f.directory,
80
+ NO_COLOR: '1',
81
+ NODE_V8_COVERAGE: coverage,
82
+ },
83
+ encoding: 'utf8',
84
+ maxBuffer: 512 * 1024,
85
+ });
86
+ assert.ok(parent.status === 0, 'Synthetic coverage parent failed before its bounded receipt');
87
+ const report = JSON.parse(parent.stdout);
88
+ recordSynthetic({ kind: 'coverage-caller-env-regression', report, stderrSha256: helper.sha256(parent.stderr) });
89
+ confirmedQuiescence = !report.unresolvedOwnedChild && report.phases.every((phase) => phase.quiescent);
90
+ assert.equal(report.actualRuntime, 'v24.21.0');
91
+ assert.equal(report.coverageParentEnabled, true);
92
+ assert.equal(report.status, 'passed');
93
+ assert.equal(report.frozenCaller, frozen);
94
+ assert.equal(report.callerKeysUnchanged, true);
95
+ assert.equal(report.callerValuesUnchanged, true);
96
+ assert.equal(report.environmentValuesSerialized, false);
97
+ assert.equal(report.unresolvedOwnedChild, false);
98
+ assert.equal(report.phases.length, 2);
99
+ assert.ok(report.phases.every((phase) => phase.quiescent));
100
+ assert.deepEqual(report.rejectedKeys, [
101
+ 'NODE_V8_COVERAGE',
102
+ 'GH_TOKEN',
103
+ 'GH_APP_ID_GPT_REVIEWER',
104
+ 'AI_DELIVERY_CONFIG',
105
+ 'NODE_PATH',
106
+ ]);
107
+ }
108
+ finally {
109
+ if (confirmedQuiescence)
110
+ f.cleanup();
111
+ else
112
+ recordSynthetic({ kind: 'fixture-retained', directory: f.directory, quiescent: false });
113
+ }
114
+ });
115
+ function archive(members) {
116
+ const chunks = [];
117
+ for (const member of members) {
118
+ const header = Buffer.alloc(512);
119
+ header.write(`package/${member.path}`, 0, 100);
120
+ const number = (value, offset, length) => header.write(value.toString(8).padStart(length - 1, '0') + '\0', offset, length);
121
+ number(member.mode, 100, 8);
122
+ number(0, 108, 8);
123
+ number(0, 116, 8);
124
+ number(member.bytes.length, 124, 12);
125
+ number(0, 136, 12);
126
+ header.fill(32, 148, 156);
127
+ header.write(member.type ?? '0', 156);
128
+ header.write('ustar\0', 257);
129
+ header.write('00', 263);
130
+ const sum = header.reduce((a, b) => a + b, 0);
131
+ header.write(sum.toString(8).padStart(6, '0') + '\0 ', 148, 8);
132
+ chunks.push(header, member.bytes, Buffer.alloc((512 - (member.bytes.length % 512)) % 512));
133
+ }
134
+ chunks.push(Buffer.alloc(1024));
135
+ return gzipSync(Buffer.concat(chunks));
136
+ }
137
+ function fixture() {
138
+ const directory = mkdtempSync(join(tmpdir(), 'synthetic-current-consumer-'));
139
+ const root = join(directory, 'built');
140
+ mkdirSync(root);
141
+ const manifest = {
142
+ name: '@aviaratech/ai-delivery',
143
+ version: '0.0.0',
144
+ type: 'module',
145
+ main: './dist/index.js',
146
+ bin: { 'ai-delivery': './dist/cli.js' },
147
+ engines: { node: '>=24.21.0 <25 || 26.2.0', npm: '11.19.0' },
148
+ exports: {
149
+ '.': { default: './dist/index.js', types: './dist/index.d.ts' },
150
+ './agent': { default: './dist/agent.js', types: './dist/agent.d.ts' },
151
+ './delivery': { default: './dist/delivery/index.js', types: './dist/delivery/index.d.ts' },
152
+ './mcp': { default: './dist/mcp/index.js', types: './dist/mcp/index.d.ts' },
153
+ },
154
+ };
155
+ const files = new Map();
156
+ files.set('package.json', JSON.stringify(manifest));
157
+ for (const path of [
158
+ 'LICENSE',
159
+ 'README.md',
160
+ 'CONTRIBUTING.md',
161
+ 'dist/cli.js',
162
+ 'plugins/ai-delivery/dist/mcp-launcher.js',
163
+ 'plugins/ai-delivery/runtime/dist/cli.js',
164
+ 'plugins/ai-delivery/runtime/dist/THIRD-PARTY-NOTICES.md',
165
+ ])
166
+ files.set(path, 'synthetic inert file\n');
167
+ for (const path of ['index', 'agent', 'delivery/index', 'mcp/index'])
168
+ for (const ext of ['js', 'd.ts'])
169
+ files.set(`dist/${path}.${ext}`, 'synthetic inert export\n');
170
+ for (const path of ['plugins/ai-delivery/plugin.json', 'plugins/ai-delivery/.claude-plugin/plugin.json'])
171
+ files.set(path, JSON.stringify({ name: 'ai-delivery', version: '0.0.0' }));
172
+ files.set('plugins/ai-delivery/runtime/package.json', JSON.stringify({ version: '0.0.0' }));
173
+ files.set('plugins/ai-delivery/mcp.json', JSON.stringify({
174
+ mcpServers: { 'ai-delivery': { type: 'stdio', command: 'node', args: ['${PLUGIN_ROOT}/dist/mcp-launcher.js'] } },
175
+ }));
176
+ for (const skill of ['intake-create', 'worktree-lifecycle', 'pr-handoff'])
177
+ files.set(`plugins/ai-delivery/skills/${skill}/SKILL.md`, `---\nname: "ai-delivery:${skill}"\ndescription: Synthetic fixture only\n---\n`);
178
+ const members = [...files].map(([path, text]) => ({
179
+ path,
180
+ bytes: Buffer.from(text),
181
+ mode: path.endsWith('/cli.js') || path.endsWith('/mcp-launcher.js') ? 0o755 : 0o644,
182
+ }));
183
+ for (const member of members) {
184
+ const path = join(root, member.path);
185
+ mkdirSync(dirname(path), { recursive: true });
186
+ writeFileSync(path, member.bytes);
187
+ chmodSync(path, member.mode);
188
+ }
189
+ writeFileSync(join(root, 'package-lock.json'), JSON.stringify({ name: manifest.name, version: manifest.version, lockfileVersion: 3, synthetic: true }));
190
+ const make = (items = members) => {
191
+ const bytes = archive(items);
192
+ const archivePath = join(directory, 'synthetic.tgz');
193
+ writeFileSync(archivePath, bytes);
194
+ const contract = {
195
+ sourceRoot: root,
196
+ archivePath,
197
+ archiveSha256: helper.sha256(bytes),
198
+ sourceManifestSha256: helper.sha256(readFileSync(join(root, 'package.json'))),
199
+ sourceLockSha256: helper.sha256(readFileSync(join(root, 'package-lock.json'))),
200
+ packageVersion: '0.0.0',
201
+ dryInventory: items.map(({ path, bytes: data, mode }) => ({ path, size: data.length, mode })),
202
+ };
203
+ recordSynthetic({ kind: 'archive-contract', ...contract });
204
+ return contract;
205
+ };
206
+ return { directory, root, members, make, cleanup: () => rmSync(directory, { recursive: true, force: true }) };
207
+ }
208
+ test('synthetic current-layout archive binds all inventory bytes/modes; no actual candidate is qualified', async () => {
209
+ const f = fixture();
210
+ try {
211
+ const contract = f.make();
212
+ const result = helper.inspectCandidate(contract);
213
+ assert.equal(result.inventory.length, f.members.length);
214
+ assert.match(result.inventorySha256, /^[a-f0-9]{64}$/u);
215
+ const deferred = await helper.runCurrentConsumer(contract);
216
+ assert.equal(deferred.status, 'incomplete');
217
+ assert.equal(deferred.qualified, false);
218
+ assert.match(deferred.reason, /not been admitted/u);
219
+ recordSynthetic({ kind: 'noninstalling-helper-result', receipt: deferred });
220
+ }
221
+ finally {
222
+ f.cleanup();
223
+ }
224
+ });
225
+ test.each(['../escape', '/absolute', 'dist/../../escape', 'dist\\escape', 'dist/.env', 'other/file'])('synthetic unsafe inventory rejects %s', (path) => {
226
+ const f = fixture();
227
+ try {
228
+ const contract = f.make([{ path, bytes: Buffer.from('unsafe'), mode: 0o644 }]);
229
+ assert.throws(() => helper.readArchive(readFileSync(contract.archivePath), contract.archiveSha256), failure('unsafe-inventory'));
230
+ }
231
+ finally {
232
+ f.cleanup();
233
+ }
234
+ });
235
+ function producerFixture() {
236
+ const f = fixture();
237
+ const contract = f.make();
238
+ const init = spawnSync('git', ['init', '--quiet'], {
239
+ cwd: f.root,
240
+ env: { PATH: '/usr/bin:/bin', GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null' },
241
+ encoding: 'utf8',
242
+ });
243
+ assert.equal(init.status, 0, init.stderr);
244
+ contract.sourceCommit = 'a'.repeat(40);
245
+ contract.sourceTree = 'b'.repeat(40);
246
+ contract.node24 = process.execPath;
247
+ contract.node26 = '/fictional/node26';
248
+ contract.npmCli = '/fictional/npm-cli.js';
249
+ const files = spawnSync('git', ['ls-files', '-co', '--exclude-standard', '-z'], {
250
+ cwd: f.root,
251
+ env: { PATH: '/usr/bin:/bin', GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null' },
252
+ encoding: 'utf8',
253
+ });
254
+ assert.equal(files.status, 0);
255
+ const fingerprint = {
256
+ kind: 'working-copy',
257
+ sha256: helper.sha256(JSON.stringify([...new Set(files.stdout.split('\0').filter(Boolean))].sort().map((path) => ({
258
+ path,
259
+ executable: f.members.some((member) => member.path === path && (member.mode & 0o111) !== 0),
260
+ sha256: helper.sha256(readFileSync(join(f.root, path))),
261
+ })))),
262
+ fileCount: new Set(files.stdout.split('\0').filter(Boolean)).size,
263
+ };
264
+ const gates = ['format', 'lint', 'types', 'build', 'tests', 'inventory'];
265
+ const checks = {
266
+ schemaVersion: 'contributor-checks@1',
267
+ runId: 'fictional-producer-run',
268
+ scope: 'full',
269
+ status: 'passed',
270
+ fullSuccess: true,
271
+ exitCode: 0,
272
+ selectedTestFiles: ['dist/fictional.test.js'],
273
+ tests: { files: 1, total: 1, passed: 1, skips: [] },
274
+ gates,
275
+ omitted: [],
276
+ tree: fingerprint,
277
+ commands: gates.map((stage) => ({ stage, status: 'passed', exitCode: 0, signal: null, cleanupConfirmed: true })),
278
+ toolchain: {
279
+ controller: { version: 'v24.21.0', executable: contract.node24 },
280
+ npm: { version: '11.19.0', executable: contract.npmCli },
281
+ libraryConsumer: { version: 'v26.2.0', executable: contract.node26 },
282
+ },
283
+ inventory: {
284
+ kind: 'dry-inventory-only',
285
+ name: '@aviaratech/ai-delivery',
286
+ version: '0.0.0',
287
+ fileCount: contract.dryInventory.length,
288
+ files: contract.dryInventory,
289
+ },
290
+ };
291
+ const artifact = {
292
+ schemaVersion: 'ai-delivery.current-artifact-producer@1',
293
+ status: 'passed',
294
+ sourceCommit: contract.sourceCommit,
295
+ sourceTree: contract.sourceTree,
296
+ sourceManifestSha256: contract.sourceManifestSha256,
297
+ sourceLockSha256: contract.sourceLockSha256,
298
+ packageVersion: contract.packageVersion,
299
+ archiveSha256: contract.archiveSha256,
300
+ inventorySha256: helper.inspectCandidate(contract).inventorySha256,
301
+ sourceFingerprint: fingerprint,
302
+ pack: { status: 'passed', exitCode: 0, quiescent: true, archiveSha256: contract.archiveSha256 },
303
+ };
304
+ const persist = () => {
305
+ const checksPath = join(f.directory, 'checks.json');
306
+ writeFileSync(checksPath, JSON.stringify(checks));
307
+ const digest = helper.sha256(readFileSync(checksPath));
308
+ artifact['checksResultSha256'] = digest;
309
+ const artifactPath = join(f.directory, 'producer.json');
310
+ writeFileSync(artifactPath, JSON.stringify(artifact));
311
+ contract.producer = {
312
+ checksResultPath: checksPath,
313
+ checksResultSha256: digest,
314
+ artifactReceiptPath: artifactPath,
315
+ artifactReceiptSha256: helper.sha256(readFileSync(artifactPath)),
316
+ };
317
+ };
318
+ persist();
319
+ return { f, contract, checks, artifact, persist };
320
+ }
321
+ test('synthetic canonical producer join accepts matching source and archive, but never qualifies without install admission', async () => {
322
+ const { f, contract } = producerFixture();
323
+ try {
324
+ const joined = helper.verifyProducerJoin(contract, helper.inspectCandidate(contract));
325
+ assert.equal(joined['actualArchiveSha256'], contract.archiveSha256);
326
+ const result = await helper.runCurrentConsumer(contract);
327
+ assert.equal(result.status, 'incomplete');
328
+ assert.equal(result.qualified, false);
329
+ recordSynthetic({ kind: 'fictional-canonical-producer-join', joined, result, actualApplicationBoundary: false });
330
+ }
331
+ finally {
332
+ f.cleanup();
333
+ }
334
+ });
335
+ test.each([
336
+ 'scope',
337
+ 'status',
338
+ 'fullSuccess',
339
+ 'exitCode',
340
+ 'gates',
341
+ 'omitted',
342
+ 'tree',
343
+ 'commands',
344
+ 'toolchain',
345
+ 'inventory',
346
+ 'tests',
347
+ 'selectedTestFiles',
348
+ ])('synthetic canonical %s incompleteness cannot qualify rebuilt artifact', (field) => {
349
+ const { f, contract, checks, persist } = producerFixture();
350
+ try {
351
+ checks[field] = field === 'gates' || field === 'omitted' ? ['build'] : null;
352
+ persist();
353
+ assert.throws(() => helper.verifyProducerJoin(contract, helper.inspectCandidate(contract)), failure('producer'));
354
+ }
355
+ finally {
356
+ f.cleanup();
357
+ }
358
+ });
359
+ test.each([
360
+ 'sourceCommit',
361
+ 'sourceTree',
362
+ 'sourceManifestSha256',
363
+ 'sourceLockSha256',
364
+ 'packageVersion',
365
+ 'archiveSha256',
366
+ 'inventorySha256',
367
+ 'pack',
368
+ 'sourceFingerprint',
369
+ ])('synthetic artifact producer %s drift cannot reuse old producer proof', (field) => {
370
+ const { f, contract, artifact, persist } = producerFixture();
371
+ try {
372
+ artifact[field] = null;
373
+ persist();
374
+ assert.throws(() => helper.verifyProducerJoin(contract, helper.inspectCandidate(contract)), failure('producer'));
375
+ }
376
+ finally {
377
+ f.cleanup();
378
+ }
379
+ });
380
+ test('synthetic producer hash and source-file drift are refused before any install', () => {
381
+ const { f, contract, persist } = producerFixture();
382
+ try {
383
+ assert.ok(contract.producer);
384
+ appendFileSync(contract.producer.checksResultPath, ' ');
385
+ assert.throws(() => helper.verifyProducerJoin(contract, helper.inspectCandidate(contract)), failure('producer'));
386
+ persist();
387
+ writeFileSync(join(f.root, 'changed-untracked-source.ts'), 'synthetic new source');
388
+ assert.throws(() => helper.verifyProducerJoin(contract, helper.inspectCandidate(contract)), failure('producer'));
389
+ delete contract.producer;
390
+ assert.throws(() => helper.verifyProducerJoin(contract, helper.inspectCandidate(contract)), failure('producer'));
391
+ }
392
+ finally {
393
+ f.cleanup();
394
+ }
395
+ });
396
+ test.each([
397
+ 'missing-gate',
398
+ 'duplicate-gate',
399
+ 'nonquiescent',
400
+ 'zero-tests',
401
+ 'zero-selection',
402
+ 'unexpected-skip',
403
+ 'dry-inventory-byte-drift',
404
+ ])('synthetic canonical producer refuses %s despite a claimed full success', (kind) => {
405
+ const { f, contract, checks, persist } = producerFixture();
406
+ try {
407
+ const steps = checks['commands'];
408
+ if (kind === 'missing-gate')
409
+ steps.pop();
410
+ else if (kind === 'duplicate-gate')
411
+ steps[5] = steps[4];
412
+ else if (kind === 'nonquiescent')
413
+ steps[3]['cleanupConfirmed'] = false;
414
+ else if (kind === 'zero-tests')
415
+ checks['tests'] = { files: 1, total: 0, passed: 0, skips: [] };
416
+ else if (kind === 'zero-selection') {
417
+ checks['selectedTestFiles'] = [];
418
+ checks['tests'] = { files: 0, total: 1, passed: 1, skips: [] };
419
+ }
420
+ else if (kind === 'unexpected-skip')
421
+ checks['tests'] = {
422
+ files: 1,
423
+ total: 2,
424
+ passed: 1,
425
+ skips: [{ file: 'dist/fictional.test.js', title: 'unreviewed skip', kind: 'unreviewed' }],
426
+ };
427
+ else
428
+ checks['inventory'] = {
429
+ kind: 'dry-inventory-only',
430
+ name: '@aviaratech/ai-delivery',
431
+ version: '0.0.0',
432
+ fileCount: 1,
433
+ files: [{ path: 'package.json', size: 1, mode: 0o644 }],
434
+ };
435
+ persist();
436
+ assert.throws(() => helper.verifyProducerJoin(contract, helper.inspectCandidate(contract)), failure('producer'));
437
+ }
438
+ finally {
439
+ f.cleanup();
440
+ }
441
+ });
442
+ test.each(['1', '2', '3', '6'])('synthetic tar link/special type %s cannot reach npm extraction', (type) => {
443
+ const f = fixture();
444
+ try {
445
+ const contract = f.make([{ path: 'dist/link', bytes: Buffer.alloc(0), mode: 0o644, type }]);
446
+ assert.throws(() => helper.readArchive(readFileSync(contract.archivePath), contract.archiveSha256), failure('unsafe-inventory'));
447
+ }
448
+ finally {
449
+ f.cleanup();
450
+ }
451
+ });
452
+ test('synthetic corruption, duplicates, inventory omissions and built-byte drift fail closed', () => {
453
+ const f = fixture();
454
+ try {
455
+ let contract = f.make();
456
+ assert.throws(() => helper.readArchive(readFileSync(contract.archivePath), 'a'.repeat(64)), failure('archive-digest'));
457
+ contract = f.make([...f.members, f.members[0]]);
458
+ assert.throws(() => helper.inspectCandidate(contract), failure('unsafe-inventory'));
459
+ contract = f.make();
460
+ contract.dryInventory.pop();
461
+ assert.throws(() => helper.inspectCandidate(contract), failure('inventory-mismatch'));
462
+ contract = f.make();
463
+ writeFileSync(join(f.root, 'README.md'), 'changed');
464
+ assert.throws(() => helper.inspectCandidate(contract), failure('built-bytes'));
465
+ }
466
+ finally {
467
+ f.cleanup();
468
+ }
469
+ });
470
+ test('synthetic installed package rejects unexpected files beyond the accepted inventory', () => {
471
+ const f = fixture();
472
+ try {
473
+ const candidate = helper.inspectCandidate(f.make());
474
+ rmSync(join(f.root, 'package-lock.json'));
475
+ helper.verifyInstalledPackage(f.root, candidate.inventory);
476
+ writeFileSync(join(f.root, 'dist', 'unexpected.js'), 'unexpected');
477
+ assert.throws(() => helper.verifyInstalledPackage(f.root, candidate.inventory), failure('installed-bytes'));
478
+ }
479
+ finally {
480
+ f.cleanup();
481
+ }
482
+ });
483
+ test.each([
484
+ 'dist/index.js',
485
+ 'plugins/ai-delivery/runtime/dist/cli.js',
486
+ 'plugins/ai-delivery/skills/intake-create/SKILL.md',
487
+ ])('synthetic missing packaged entry %s fails inspection', (path) => {
488
+ const f = fixture();
489
+ try {
490
+ assert.throws(() => helper.inspectCandidate(f.make(f.members.filter((member) => member.path !== path))), failure('missing-file'));
491
+ }
492
+ finally {
493
+ f.cleanup();
494
+ }
495
+ });
496
+ test.each(['name', 'version', 'runtime'])('synthetic wrong package %s cannot pass', (kind) => {
497
+ const f = fixture();
498
+ try {
499
+ const member = f.members.find((item) => item.path === 'package.json');
500
+ const pkg = JSON.parse(member.bytes.toString());
501
+ if (kind === 'name')
502
+ pkg.name = 'other';
503
+ else if (kind === 'version')
504
+ pkg.version = '0.0.1';
505
+ else
506
+ pkg.engines.node = '>=20';
507
+ member.bytes = Buffer.from(JSON.stringify(pkg));
508
+ assert.throws(() => helper.inspectCandidate(f.make()), failure(kind === 'runtime' ? 'runtime' : 'package-identity'));
509
+ }
510
+ finally {
511
+ f.cleanup();
512
+ }
513
+ });
514
+ test('synthetic environment never borrows ambient author/reviewer/preload or npm configuration', () => {
515
+ const f = fixture();
516
+ try {
517
+ const env = helper.consumerEnvironment(f.directory, process.execPath);
518
+ for (const name of [
519
+ 'GH_TOKEN',
520
+ 'GH_APP_ID_GPT_REVIEWER',
521
+ 'GH_INSTALLATION_ID_GPT_REVIEWER',
522
+ 'GH_PRIVATE_KEY_GPT_REVIEWER',
523
+ 'NODE_PATH',
524
+ 'NODE_OPTIONS',
525
+ 'AI_DELIVERY_CONFIG',
526
+ ])
527
+ assert.equal(env[name], undefined);
528
+ assert.equal(readFileSync(env['npm_config_userconfig'], 'utf8'), '');
529
+ assert.notEqual(env['npm_config_userconfig'], env['npm_config_globalconfig']);
530
+ }
531
+ finally {
532
+ f.cleanup();
533
+ }
534
+ });
535
+ test('synthetic resolver demonstrates a borrowing false positive, then rejects external and undeclared hoisted dependencies', async () => {
536
+ const f = fixture();
537
+ try {
538
+ const consumer = join(f.directory, 'consumer');
539
+ mkdirSync(consumer);
540
+ writeFileSync(join(consumer, 'package.json'), JSON.stringify({ type: 'module', dependencies: {} }));
541
+ const borrowed = join(f.directory, 'node_modules', 'borrowed-only');
542
+ mkdirSync(borrowed, { recursive: true });
543
+ writeFileSync(join(borrowed, 'package.json'), JSON.stringify({ name: 'borrowed-only', type: 'module', exports: './index.js' }));
544
+ writeFileSync(join(borrowed, 'index.js'), 'export const borrowed=true;');
545
+ const env = helper.consumerEnvironment(f.directory, process.execPath);
546
+ const probe = join(consumer, 'owned-probe.mjs');
547
+ writeFileSync(probe, "console.log((await import('borrowed-only')).borrowed)");
548
+ const args = [probe];
549
+ assert.equal((await helper.runOwnedProcess(process.execPath, args, { cwd: consumer, env })).stdout.trim(), 'true');
550
+ const guard = join(consumer, 'owned-guard.mjs');
551
+ writeFileSync(guard, helper.resolutionGuard(consumer));
552
+ await assert.rejects(helper.runOwnedProcess(process.execPath, ['--import', guard, ...args], { cwd: consumer, env }), failure('isolation'));
553
+ const hoisted = join(consumer, 'node_modules', 'borrowed-only');
554
+ mkdirSync(hoisted, { recursive: true });
555
+ writeFileSync(join(hoisted, 'package.json'), readFileSync(join(borrowed, 'package.json')));
556
+ writeFileSync(join(hoisted, 'index.js'), 'export const borrowed=true;');
557
+ await assert.rejects(helper.runOwnedProcess(process.execPath, ['--import', guard, ...args], { cwd: consumer, env }), failure('dependency'));
558
+ }
559
+ finally {
560
+ f.cleanup();
561
+ }
562
+ });
563
+ test.each(['network', 'permission', 'process'])('synthetic external %s failure retains bounded quiescent evidence', async (kind) => {
564
+ const f = fixture();
565
+ try {
566
+ const env = helper.consumerEnvironment(f.directory, process.execPath);
567
+ const text = kind === 'network' ? 'ENOTFOUND' : kind === 'permission' ? 'EACCES' : 'synthetic npm failure';
568
+ await assert.rejects(helper.runOwnedProcess(process.execPath, ['-e', `process.stderr.write(${JSON.stringify(text)});process.exit(1)`], { cwd: f.directory, env }), (error) => failure(kind)(error) && error.evidence.quiescent === true);
569
+ assert.throws(() => helper.parseJsonOutput('startup banner\n{}'), failure('schema'));
570
+ }
571
+ finally {
572
+ f.cleanup();
573
+ }
574
+ });
575
+ test('synthetic interrupted owned child is quiescent before disposable fixture cleanup', async () => {
576
+ const f = fixture();
577
+ const cancellation = new AbortController();
578
+ try {
579
+ const env = helper.consumerEnvironment(f.directory, process.execPath);
580
+ await assert.rejects(helper.runOwnedProcess(process.execPath, ['-e', 'setInterval(()=>{},1000)'], {
581
+ cwd: f.directory,
582
+ env,
583
+ signal: cancellation.signal,
584
+ onSpawn: () => setTimeout(() => cancellation.abort(), 50),
585
+ }), (error) => {
586
+ assert.equal(error.code, 'cancelled');
587
+ assert.equal(error.evidence.quiescent, true);
588
+ assert.ok(error.evidence.birth);
589
+ return true;
590
+ });
591
+ }
592
+ finally {
593
+ f.cleanup();
594
+ }
595
+ });
596
+ test('synthetic bounded-output failure never becomes successful stdout/schema proof', async () => {
597
+ const f = fixture();
598
+ try {
599
+ await assert.rejects(helper.runOwnedProcess(process.execPath, ['-e', "process.stdout.write('x'.repeat(10000))"], {
600
+ cwd: f.directory,
601
+ env: helper.consumerEnvironment(f.directory, process.execPath),
602
+ maxOutputBytes: 128,
603
+ }), failure('output-bound'));
604
+ }
605
+ finally {
606
+ f.cleanup();
607
+ }
608
+ });
609
+ test.each([
610
+ '---\nname: "ai-delivery:intake-create"\ndescription: Synthetic fixture only\n',
611
+ '---\nother: Header\n---\nname: "ai-delivery:intake-create"\ndescription: Body only\n',
612
+ ])('synthetic malformed skill frontmatter fails within the delimited header', (text) => {
613
+ const f = fixture();
614
+ try {
615
+ const member = f.members.find((item) => item.path === 'plugins/ai-delivery/skills/intake-create/SKILL.md');
616
+ member.bytes = Buffer.from(text);
617
+ assert.throws(() => helper.inspectCandidate(f.make()), failure('schema'));
618
+ }
619
+ finally {
620
+ f.cleanup();
621
+ }
622
+ });
623
+ test('synthetic nested type-only manifest uses the installed package declaration owner', async () => {
624
+ const f = fixture();
625
+ try {
626
+ const consumer = join(f.directory, 'consumer');
627
+ mkdirSync(consumer);
628
+ writeFileSync(join(consumer, 'package.json'), JSON.stringify({ name: 'fictional-consumer', type: 'module', dependencies: { '@fictional/sdk': '0' } }));
629
+ const sdk = join(consumer, 'node_modules', '@fictional', 'sdk');
630
+ const esm = join(sdk, 'dist', 'esm');
631
+ mkdirSync(esm, { recursive: true });
632
+ writeFileSync(join(sdk, 'package.json'), JSON.stringify({
633
+ name: '@fictional/sdk',
634
+ type: 'module',
635
+ exports: './dist/esm/index.js',
636
+ dependencies: { 'declared-only': '0' },
637
+ }));
638
+ writeFileSync(join(esm, 'package.json'), '{"type":"module"}');
639
+ const dep = join(sdk, 'node_modules', 'declared-only');
640
+ mkdirSync(dep, { recursive: true });
641
+ writeFileSync(join(dep, 'package.json'), JSON.stringify({ name: 'declared-only', type: 'module', exports: './index.js' }));
642
+ writeFileSync(join(dep, 'index.js'), 'export const value=true;');
643
+ writeFileSync(join(esm, 'index.js'), "import {value} from 'declared-only';console.log(value);");
644
+ const guard = join(consumer, 'owned-guard.mjs');
645
+ writeFileSync(guard, helper.resolutionGuard(consumer));
646
+ const env = helper.consumerEnvironment(f.directory, process.execPath);
647
+ const probe = join(consumer, 'owned-probe.mjs');
648
+ writeFileSync(probe, "await import('@fictional/sdk')");
649
+ const args = ['--import', guard, probe];
650
+ assert.equal((await helper.runOwnedProcess(process.execPath, args, { cwd: consumer, env })).stdout.trim(), 'true');
651
+ writeFileSync(join(esm, 'index.js'), "await import('missing-only');");
652
+ await assert.rejects(helper.runOwnedProcess(process.execPath, args, { cwd: consumer, env }), failure('dependency'));
653
+ const hoisted = join(consumer, 'node_modules', 'undeclared-only');
654
+ mkdirSync(hoisted, { recursive: true });
655
+ writeFileSync(join(hoisted, 'package.json'), JSON.stringify({ name: 'undeclared-only', type: 'module', exports: './index.js' }));
656
+ writeFileSync(join(hoisted, 'index.js'), 'export const value=true;');
657
+ writeFileSync(join(esm, 'index.js'), "await import('undeclared-only');");
658
+ await assert.rejects(helper.runOwnedProcess(process.execPath, args, { cwd: consumer, env }), failure('dependency'));
659
+ }
660
+ finally {
661
+ f.cleanup();
662
+ }
663
+ });
664
+ test('synthetic cancellation kills an observed resistant descendant after its parent exits', async () => {
665
+ const f = fixture();
666
+ const cancellation = new AbortController();
667
+ let quiescent = false;
668
+ try {
669
+ const env = helper.consumerEnvironment(f.directory, process.execPath);
670
+ const descendant = join(f.directory, 'descendant.cjs');
671
+ writeFileSync(descendant, "process.on('SIGTERM',()=>{});require('node:fs').writeFileSync('ready',String(process.pid));setInterval(()=>{},1000);");
672
+ const parent = join(f.directory, 'parent.cjs');
673
+ writeFileSync(parent, "require('node:child_process').spawn(process.execPath,[process.argv[2]],{stdio:['ignore',process.stdout,process.stderr]});setInterval(()=>{},1000);");
674
+ const running = helper.runOwnedProcess(process.execPath, [parent, descendant], {
675
+ cwd: f.directory,
676
+ env,
677
+ signal: cancellation.signal,
678
+ });
679
+ // Synchronize to the child's ready marker; allow one 50ms identity sample.
680
+ for (let i = 0; i < 100; i++) {
681
+ try {
682
+ readFileSync(join(f.directory, 'ready'));
683
+ break;
684
+ }
685
+ catch {
686
+ await delay(10);
687
+ }
688
+ }
689
+ await delay(150);
690
+ cancellation.abort();
691
+ await assert.rejects(running, (error) => {
692
+ const e = error;
693
+ quiescent = e.evidence.quiescent === true;
694
+ assert.equal(e.code, 'cancelled');
695
+ assert.equal(quiescent, true);
696
+ assert.ok((e.evidence.observed?.length ?? 0) >= 2);
697
+ return true;
698
+ });
699
+ }
700
+ finally {
701
+ if (quiescent)
702
+ f.cleanup();
703
+ }
704
+ }, 5000);
705
+ test('synthetic unknown or reused process identity is refused rather than signaled', () => {
706
+ const known = [{ pid: 1, birth: 'first', rssBytes: 0 }];
707
+ assert.deepEqual(helper.ownedSignalTargets(known, null), { targets: [], unknown: true });
708
+ assert.deepEqual(helper.ownedSignalTargets(known, [{ pid: 1, birth: 'reused', rssBytes: 0 }]), {
709
+ targets: [],
710
+ unknown: true,
711
+ });
712
+ });
713
+ test.each(['created', 'prepared', 'cleanup', 'unknown-quiescence'])('synthetic disposable %s failure preserves receipt and owned cleanup evidence', async (kind) => {
714
+ const f = fixture();
715
+ let retained;
716
+ try {
717
+ const receipt = {
718
+ status: 'incomplete',
719
+ qualified: false,
720
+ phases: [{ phase: 'prior', synthetic: true }],
721
+ cleanup: null,
722
+ };
723
+ const result = await helper.withDisposableConsumer(f.root, process.execPath, 'a'.repeat(64), receipt, async (context) => {
724
+ if (kind === 'unknown-quiescence')
725
+ context.setQuiescent(false);
726
+ return receipt;
727
+ }, {
728
+ fault: (phase, directory) => {
729
+ retained = directory;
730
+ if (kind === 'cleanup' && phase === 'cleanup')
731
+ writeFileSync(join(directory, 'owned.json'), 'corrupt marker');
732
+ if (kind === phase && (kind === 'created' || kind === 'prepared'))
733
+ throw Object.assign(new Error('fictional permission failure'), { code: 'EACCES' });
734
+ },
735
+ });
736
+ assert.equal(result.status, 'incomplete');
737
+ assert.equal(result.qualified, false);
738
+ assert.deepEqual(result.phases, [{ phase: 'prior', synthetic: true }]);
739
+ assert.equal(result.cleanup?.removed, kind === 'created' || kind === 'prepared');
740
+ if (kind === 'cleanup')
741
+ assert.ok(result.cleanup?.failure);
742
+ if (kind === 'unknown-quiescence')
743
+ assert.equal(result.cleanup?.quiescent, false);
744
+ }
745
+ finally {
746
+ // These are explicit injected filesystem/quiescence faults with no processes.
747
+ if (retained)
748
+ rmSync(retained, { recursive: true, force: true });
749
+ f.cleanup();
750
+ }
751
+ });
752
+ test.each([
753
+ 'startup',
754
+ 'schema',
755
+ 'matching-schema',
756
+ 'resistant-startup',
757
+ 'resistant-schema',
758
+ 'resistant-matching-schema',
759
+ ])('synthetic packaged stdio MCP %s exercises its explicit mocked schema and shutdown boundary', async (kind) => {
760
+ const f = fixture();
761
+ let quiescent = true;
762
+ try {
763
+ const consumer = join(f.directory, 'consumer');
764
+ mkdirSync(consumer);
765
+ writeFileSync(join(consumer, 'package.json'), JSON.stringify({ type: 'module' }));
766
+ const pkg = join(consumer, 'node_modules', '@aviaratech', 'ai-delivery');
767
+ mkdirSync(pkg, { recursive: true });
768
+ writeFileSync(join(pkg, 'package.json'), JSON.stringify({ name: '@aviaratech/ai-delivery', type: 'module', exports: { './mcp': './mcp.js' } }));
769
+ writeFileSync(join(pkg, 'mcp.js'), 'export const AI_DELIVERY_MCP_TOOLS=[];');
770
+ const zod = join(pkg, 'node_modules', 'zod');
771
+ mkdirSync(zod, { recursive: true });
772
+ writeFileSync(join(zod, 'package.json'), JSON.stringify({ name: 'zod', main: 'index.cjs' }));
773
+ writeFileSync(join(zod, 'index.cjs'), 'exports.toJSONSchema=()=>({});');
774
+ const launcher = join(consumer, 'fictional-packaged-launcher.cjs');
775
+ const startup = kind === 'startup';
776
+ const resistant = kind.startsWith('resistant-');
777
+ const tools = ['matching-schema', 'resistant-matching-schema'].includes(kind)
778
+ ? []
779
+ : [{ name: 'unexpected', inputSchema: {} }];
780
+ const server = `process.on('SIGTERM',${resistant ? '()=>{}' : '()=>process.exit(0)'});
781
+ require('node:readline').createInterface({input:process.stdin}).on('line',line=>{
782
+ const m=JSON.parse(line);if(m.id===1)console.log(JSON.stringify({jsonrpc:'2.0',id:1,result:{serverInfo:{version:'0.0.0'}}}));
783
+ if(m.id===2)console.log(JSON.stringify({jsonrpc:'2.0',id:2,result:{tools:${JSON.stringify(tools)}}}));
784
+ });`;
785
+ writeFileSync(launcher, startup
786
+ ? 'process.exit(1);'
787
+ : kind === 'resistant-startup'
788
+ ? "process.on('SIGTERM',()=>{});console.log('fictional startup banner');setInterval(()=>{},1000);"
789
+ : server);
790
+ const probe = join(consumer, 'owned-mcp.mjs');
791
+ writeFileSync(probe, helper.MCP_PROBE);
792
+ quiescent = false;
793
+ const running = helper.runOwnedProcess(process.execPath, [probe, launcher, '0.0.0'], {
794
+ cwd: consumer,
795
+ env: helper.consumerEnvironment(f.directory, process.execPath),
796
+ });
797
+ if (kind === 'matching-schema') {
798
+ const result = await running;
799
+ quiescent = result.evidence.quiescent;
800
+ assert.deepEqual(helper.parseJsonOutput(result.stdout), {
801
+ toolCount: 0,
802
+ schemas: [],
803
+ protocol: 'stdio initialize/tools/list',
804
+ version: '0.0.0',
805
+ });
806
+ assert.equal(result.evidence.quiescent, true);
807
+ }
808
+ else
809
+ await assert.rejects(running, (error) => {
810
+ quiescent = error.evidence.quiescent === true;
811
+ return failure(kind === 'resistant-matching-schema' ? 'cleanup' : 'schema')(error) && quiescent;
812
+ });
813
+ }
814
+ finally {
815
+ if (quiescent)
816
+ f.cleanup();
817
+ else
818
+ recordSynthetic({ kind: 'fixture-retained', directory: f.directory, quiescent: false });
819
+ }
820
+ });
821
+ test.each(['sourceManifestSha256', 'sourceLockSha256'])('synthetic integrated %s digest drift cannot reuse an older archive as candidate proof', (field) => {
822
+ const f = fixture();
823
+ try {
824
+ const contract = f.make();
825
+ contract[field] = 'a'.repeat(64);
826
+ assert.throws(() => helper.inspectCandidate(contract), failure('source-identity'));
827
+ }
828
+ finally {
829
+ f.cleanup();
830
+ }
831
+ });
832
+ //# sourceMappingURL=currentConsumer.test.js.map