@aviaratech/ai-delivery 0.3.11 → 0.3.12
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/README.md
CHANGED
|
@@ -336,7 +336,7 @@ The `--admit` flag explicitly admits additional resource classes for execution.
|
|
|
336
336
|
|
|
337
337
|
For a bounded run, pass `--max-aggregate-rss-bytes`, `--min-free-disk-bytes`, and optionally `--max-new-output-bytes` with one or more `--output-root` paths to `verify`. `issue_verify` accepts the same values in `resourceBounds`. RSS is sampled across the observed child process tree, including observed detached descendants; free disk is checked before commands and during execution on the worktree and declared output filesystems. Positive file-size growth is sampled under the declared roots, with previously measured completed stages carried across a resume. The caller must declare every output root relevant to its allowance; aliases must resolve within those roots. The roots are relative to the issue worktree unless absolute. Output growth is separate from the 8 MiB captured stdout/stderr limit. Resource limits are opt in and do not impose a total runtime deadline.
|
|
338
338
|
|
|
339
|
-
During a running command,
|
|
339
|
+
During a running command, a previously validated, unchanged alias may temporarily lose its physical target during a rebuild. The observer checks its current identity and target ancestry, reports the path and errno, and still completes a fresh scan of all physical files in every declared root. It never substitutes an incomplete scan or an earlier byte count. A missing target does not impose a build deadline when complete output accounting remains available. Unvalidated aliases that prevent a complete scan retain the one-second observation-stall window, starting after the first failed scan rather than before it. Cancellation, RSS and disk checks continue. Baseline and final scans remain strict; permanent broken links, cycles, escaping aliases and changed identities fail closed. Failed commands retain captured stdout/stderr in the existing private content-addressed command-output store, with its digest, actual exit status and first/last filesystem observation in the failure diagnostic. Output still buffered inside a killed child is unavailable to the runner.
|
|
340
340
|
|
|
341
341
|
Real filesystem-negative tests can use `withVerificationFilesystemFixture` from `@aviaratech/ai-delivery` (or its `agent` export):
|
|
342
342
|
|
package/dist/lifecycle.test.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { execFileSync, spawn, spawnSync } from 'node:child_process';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
|
+
import { syncBuiltinESMExports } from 'node:module';
|
|
4
5
|
import { cpSync, chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, readdirSync, realpathSync, rmSync, symlinkSync, unlinkSync, utimesSync, writeFileSync, } from 'node:fs';
|
|
5
6
|
import { tmpdir } from 'node:os';
|
|
6
7
|
import { dirname, join } from 'node:path';
|
|
@@ -4022,7 +4023,196 @@ test.each([
|
|
|
4022
4023
|
test('an unrelated consumer uses public MCP setup through verified configured-author publication, counted review readback, merge and cleanup', { timeout: 0 }, async () => {
|
|
4023
4024
|
await syntheticLifecycle({ remote: 'origin', divergentOrigin: false, producerOnboarding: true });
|
|
4024
4025
|
});
|
|
4025
|
-
|
|
4026
|
+
function ordinaryCliRebuildScript(output, mode = 'pass', target = join(output, 'cli.js')) {
|
|
4027
|
+
return `(async () => {
|
|
4028
|
+
const fs = require('node:fs');
|
|
4029
|
+
const trace = event => fs.appendFileSync(${JSON.stringify(join(output, 'events'))}, event + '\\n');
|
|
4030
|
+
trace('command-started'); fs.unlinkSync(${JSON.stringify(target)}); trace('target-removed');
|
|
4031
|
+
if (${JSON.stringify(dirname(target))} !== ${JSON.stringify(output)}) fs.rmdirSync(${JSON.stringify(dirname(target))});
|
|
4032
|
+
if (${JSON.stringify(mode)} === 'limit') fs.writeFileSync(${JSON.stringify(join(output, 'oversized'))}, 'x'.repeat(4096));
|
|
4033
|
+
for (let unit = 0; unit < 32; unit++) {
|
|
4034
|
+
fs.appendFileSync(${JSON.stringify(join(output, 'compiled'))}, 'build-unit\\n');
|
|
4035
|
+
trace('build-unit-' + unit);
|
|
4036
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
4037
|
+
}
|
|
4038
|
+
if (${JSON.stringify(mode)} !== 'absent') {
|
|
4039
|
+
fs.mkdirSync(${JSON.stringify(dirname(target))}, { recursive: true });
|
|
4040
|
+
fs.writeFileSync(${JSON.stringify(target)}, 'rebuilt CLI'); trace('target-restored');
|
|
4041
|
+
}
|
|
4042
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });`;
|
|
4043
|
+
}
|
|
4044
|
+
test.each(['direct', 'chain', 'directory'])('ordinary bounded rebuild restores its %s CLI target after observable build work', async (shape) => {
|
|
4045
|
+
const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-cli-rebuild-')));
|
|
4046
|
+
const { root } = await fixture();
|
|
4047
|
+
const target = shape === 'directory' ? join(output, 'dist', 'cli.js') : join(output, 'cli.js');
|
|
4048
|
+
const alias = join(output, 'cli');
|
|
4049
|
+
const events = join(output, 'events');
|
|
4050
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
4051
|
+
writeFileSync(target, 'original CLI');
|
|
4052
|
+
writeFileSync(join(output, 'unrelated'), 'preserved');
|
|
4053
|
+
if (shape === 'direct')
|
|
4054
|
+
symlinkSync('./cli.js', alias);
|
|
4055
|
+
else {
|
|
4056
|
+
symlinkSync(shape === 'chain' ? './cli.js' : './dist', join(output, 'intermediate'));
|
|
4057
|
+
symlinkSync(shape === 'chain' ? './intermediate' : './intermediate/cli.js', alias);
|
|
4058
|
+
}
|
|
4059
|
+
const observations = [];
|
|
4060
|
+
const duringAbsence = [];
|
|
4061
|
+
const progress = vi.spyOn(process.stderr, 'write').mockImplementation((line) => {
|
|
4062
|
+
observations.push(String(line));
|
|
4063
|
+
if (!existsSync(target) && String(line).includes('sampledNewOutputBytes'))
|
|
4064
|
+
duringAbsence.push(JSON.parse(String(line).slice(String(line).indexOf('{'))));
|
|
4065
|
+
return true;
|
|
4066
|
+
});
|
|
4067
|
+
const script = ordinaryCliRebuildScript(output, 'pass', target);
|
|
4068
|
+
try {
|
|
4069
|
+
const { withRuntimeSetupWriter } = await import('./verification.js');
|
|
4070
|
+
await withRuntimeSetupWriter(root, (runner) => runner.run([process.execPath, '-e', script], {
|
|
4071
|
+
maxAggregateRssBytes: 1_000_000_000,
|
|
4072
|
+
minFreeDiskBytes: 1,
|
|
4073
|
+
maxNewOutputBytes: 8192,
|
|
4074
|
+
outputRoots: [output],
|
|
4075
|
+
}));
|
|
4076
|
+
assert.equal(readFileSync(target, 'utf8'), 'rebuilt CLI');
|
|
4077
|
+
assert.equal(readFileSync(join(output, 'compiled'), 'utf8').split('\n').length, 33);
|
|
4078
|
+
assert.equal(readFileSync(join(output, 'unrelated'), 'utf8'), 'preserved');
|
|
4079
|
+
assert.ok(duringAbsence.some((sample) => (sample.sampledNewOutputBytes ?? 0) > 0));
|
|
4080
|
+
assert.ok(duringAbsence.every((sample) => (sample.sampledAggregateRssBytes ?? 0) > 0));
|
|
4081
|
+
}
|
|
4082
|
+
finally {
|
|
4083
|
+
console.log(JSON.stringify({
|
|
4084
|
+
scenario: 'ordinary-cli-rebuild',
|
|
4085
|
+
shape,
|
|
4086
|
+
firstObservation: observations.find((line) => line.includes('ENOENT')),
|
|
4087
|
+
events: readFileSync(events, 'utf8').trim().split('\n'),
|
|
4088
|
+
targetRestored: existsSync(target),
|
|
4089
|
+
unrelatedPreserved: readFileSync(join(output, 'unrelated'), 'utf8') === 'preserved',
|
|
4090
|
+
}));
|
|
4091
|
+
progress.mockRestore();
|
|
4092
|
+
rmSync(root, { recursive: true, force: true });
|
|
4093
|
+
rmSync(output, { recursive: true, force: true });
|
|
4094
|
+
}
|
|
4095
|
+
});
|
|
4096
|
+
test.each([
|
|
4097
|
+
'bytes',
|
|
4098
|
+
'rss',
|
|
4099
|
+
'disk',
|
|
4100
|
+
'fifo',
|
|
4101
|
+
'permission',
|
|
4102
|
+
'identity',
|
|
4103
|
+
'ancestor',
|
|
4104
|
+
'cycle',
|
|
4105
|
+
'chain-identity',
|
|
4106
|
+
'directory-identity',
|
|
4107
|
+
'chain-cycle',
|
|
4108
|
+
])('a missing validated target retains complete scan and %s rejection across roots', async (mode) => {
|
|
4109
|
+
const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-rebuild-boundary-')));
|
|
4110
|
+
const { root } = await fixture();
|
|
4111
|
+
const first = join(output, 'first'), second = join(output, 'second');
|
|
4112
|
+
mkdirSync(first);
|
|
4113
|
+
mkdirSync(second);
|
|
4114
|
+
const directory = join(first, 'dist'), target = join(directory, 'cli.js'), alias = join(first, 'cli');
|
|
4115
|
+
mkdirSync(directory);
|
|
4116
|
+
writeFileSync(target, 'CLI');
|
|
4117
|
+
const intermediate = join(first, 'intermediate');
|
|
4118
|
+
if (mode === 'chain-identity' || mode === 'chain-cycle') {
|
|
4119
|
+
symlinkSync('./dist/cli.js', intermediate);
|
|
4120
|
+
symlinkSync('./intermediate', alias);
|
|
4121
|
+
}
|
|
4122
|
+
else if (mode === 'directory-identity') {
|
|
4123
|
+
symlinkSync('./dist', intermediate);
|
|
4124
|
+
symlinkSync('./intermediate/cli.js', alias);
|
|
4125
|
+
}
|
|
4126
|
+
else
|
|
4127
|
+
symlinkSync('./dist/cli.js', alias);
|
|
4128
|
+
writeFileSync(join(second, 'unrelated'), 'preserved');
|
|
4129
|
+
const released = join(second, 'released');
|
|
4130
|
+
const observations = [];
|
|
4131
|
+
const progress = vi.spyOn(process.stderr, 'write').mockImplementation((line) => {
|
|
4132
|
+
observations.push(String(line));
|
|
4133
|
+
if (String(line).includes('ENOENT'))
|
|
4134
|
+
writeFileSync(released, 'observed');
|
|
4135
|
+
return true;
|
|
4136
|
+
});
|
|
4137
|
+
const script = `(async () => {
|
|
4138
|
+
const fs = require('node:fs'), cp = require('node:child_process');
|
|
4139
|
+
fs.unlinkSync(${JSON.stringify(target)});
|
|
4140
|
+
while (!fs.existsSync(${JSON.stringify(released)})) await new Promise(resolve => setTimeout(resolve, 10));
|
|
4141
|
+
if (${JSON.stringify(mode)} === 'bytes') fs.writeFileSync(${JSON.stringify(join(second, 'oversized'))}, 'x'.repeat(4096));
|
|
4142
|
+
if (${JSON.stringify(mode)} === 'rss') globalThis.retainedBuildMemory = Buffer.alloc(200 * 1024 * 1024, 1);
|
|
4143
|
+
if (${JSON.stringify(mode)} === 'disk') fs.writeFileSync(${JSON.stringify(join(second, 'disk-output'))}, Buffer.alloc(64 * 1024 * 1024, 1));
|
|
4144
|
+
if (${JSON.stringify(mode)} === 'fifo') cp.execFileSync('mkfifo', [${JSON.stringify(join(second, 'unsupported'))}]);
|
|
4145
|
+
if (${JSON.stringify(mode)} === 'permission') {
|
|
4146
|
+
fs.mkdirSync(${JSON.stringify(join(second, 'denied'))});
|
|
4147
|
+
fs.chmodSync(${JSON.stringify(join(second, 'denied'))}, 0);
|
|
4148
|
+
}
|
|
4149
|
+
if (${JSON.stringify(mode)} === 'identity') {
|
|
4150
|
+
fs.unlinkSync(${JSON.stringify(alias)}); fs.writeFileSync(${JSON.stringify(join(first, 'other'))}, 'other');
|
|
4151
|
+
fs.symlinkSync('./other', ${JSON.stringify(alias)});
|
|
4152
|
+
}
|
|
4153
|
+
if (${JSON.stringify(mode)} === 'ancestor') {
|
|
4154
|
+
fs.rmdirSync(${JSON.stringify(directory)}); fs.symlinkSync(${JSON.stringify(second)}, ${JSON.stringify(directory)});
|
|
4155
|
+
}
|
|
4156
|
+
if (${JSON.stringify(mode)} === 'cycle') fs.symlinkSync('./cli.js', ${JSON.stringify(target)});
|
|
4157
|
+
if (${JSON.stringify(mode)} === 'chain-identity') {
|
|
4158
|
+
fs.unlinkSync(${JSON.stringify(intermediate)}); fs.writeFileSync(${JSON.stringify(join(first, 'other'))}, 'other');
|
|
4159
|
+
fs.symlinkSync('./other', ${JSON.stringify(intermediate)});
|
|
4160
|
+
}
|
|
4161
|
+
if (${JSON.stringify(mode)} === 'directory-identity') {
|
|
4162
|
+
fs.unlinkSync(${JSON.stringify(intermediate)}); fs.mkdirSync(${JSON.stringify(join(first, 'other'))});
|
|
4163
|
+
fs.writeFileSync(${JSON.stringify(join(first, 'other', 'cli.js'))}, 'other');
|
|
4164
|
+
fs.symlinkSync('./other', ${JSON.stringify(intermediate)});
|
|
4165
|
+
}
|
|
4166
|
+
if (${JSON.stringify(mode)} === 'chain-cycle') {
|
|
4167
|
+
fs.unlinkSync(${JSON.stringify(intermediate)}); fs.symlinkSync('./cli', ${JSON.stringify(intermediate)});
|
|
4168
|
+
}
|
|
4169
|
+
for (let unit = 0; unit < 32; unit++) {
|
|
4170
|
+
fs.appendFileSync(${JSON.stringify(join(second, 'work'))}, 'unit\\n');
|
|
4171
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
4172
|
+
}
|
|
4173
|
+
fs.writeFileSync(${JSON.stringify(target)}, 'rebuilt CLI');
|
|
4174
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });`;
|
|
4175
|
+
try {
|
|
4176
|
+
const { withRuntimeSetupWriter } = await import('./verification.js');
|
|
4177
|
+
const disk = (await import('node:fs')).statfsSync(output);
|
|
4178
|
+
await assert.rejects(withRuntimeSetupWriter(root, (runner) => runner.run([process.execPath, '-e', script], {
|
|
4179
|
+
maxAggregateRssBytes: mode === 'rss' ? 128 * 1024 * 1024 : 1_000_000_000,
|
|
4180
|
+
minFreeDiskBytes: mode === 'disk' ? disk.bavail * disk.bsize - 32 * 1024 * 1024 : 1,
|
|
4181
|
+
maxNewOutputBytes: mode === 'disk' ? 128 * 1024 * 1024 : 1024,
|
|
4182
|
+
outputRoots: [first, second],
|
|
4183
|
+
})), (error) => {
|
|
4184
|
+
const message = String(error);
|
|
4185
|
+
assert.match(message, mode === 'bytes'
|
|
4186
|
+
? /filesystem output.*exceeded/u
|
|
4187
|
+
: mode === 'rss'
|
|
4188
|
+
? /aggregate RSS.*exceeded/u
|
|
4189
|
+
: mode === 'disk'
|
|
4190
|
+
? /Free disk fell below/u
|
|
4191
|
+
: mode === 'fifo'
|
|
4192
|
+
? /unsupported file/u
|
|
4193
|
+
: mode === 'permission'
|
|
4194
|
+
? /readdir failed.*EACCES/u
|
|
4195
|
+
: mode === 'cycle' || mode === 'chain-cycle'
|
|
4196
|
+
? /ELOOP/u
|
|
4197
|
+
: /identity changed/u);
|
|
4198
|
+
assert.match(message, /First filesystem observation:.*ENOENT.*Last filesystem observation:/u);
|
|
4199
|
+
assert.doesNotMatch(message, /cleanup failed/u);
|
|
4200
|
+
return true;
|
|
4201
|
+
});
|
|
4202
|
+
assert.ok(observations.some((line) => line.includes('ENOENT')));
|
|
4203
|
+
assert.equal(observations.some((line) => line.includes('"state":"completed"')), false);
|
|
4204
|
+
assert.equal(readFileSync(join(second, 'unrelated'), 'utf8'), 'preserved');
|
|
4205
|
+
}
|
|
4206
|
+
finally {
|
|
4207
|
+
const denied = join(second, 'denied');
|
|
4208
|
+
if (existsSync(denied))
|
|
4209
|
+
chmodSync(denied, 0o700);
|
|
4210
|
+
progress.mockRestore();
|
|
4211
|
+
rmSync(root, { recursive: true, force: true });
|
|
4212
|
+
rmSync(output, { recursive: true, force: true });
|
|
4213
|
+
}
|
|
4214
|
+
});
|
|
4215
|
+
test.each(['pass', 'chain-pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary owned symlink teardown preserves observation and %s command evidence', async (mode) => {
|
|
4026
4216
|
const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-ordinary-teardown-')));
|
|
4027
4217
|
const { root } = await fixture();
|
|
4028
4218
|
const controller = new AbortController();
|
|
@@ -4031,7 +4221,9 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
|
|
|
4031
4221
|
const release = join(output, 'release');
|
|
4032
4222
|
writeFileSync(join(output, 'unrelated'), 'preserved');
|
|
4033
4223
|
writeFileSync(join(output, 'target'), 'x'.repeat(128));
|
|
4034
|
-
|
|
4224
|
+
if (mode === 'chain-pass')
|
|
4225
|
+
symlinkSync('./target', join(output, 'intermediate'));
|
|
4226
|
+
symlinkSync(mode === 'chain-pass' ? './intermediate' : './target', join(output, 'alias'));
|
|
4035
4227
|
let observedGap = false;
|
|
4036
4228
|
const observations = [];
|
|
4037
4229
|
let releaseTimer;
|
|
@@ -4044,7 +4236,7 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
|
|
|
4044
4236
|
releaseTimer = setTimeout(() => {
|
|
4045
4237
|
if (mode === 'cancel')
|
|
4046
4238
|
controller.abort();
|
|
4047
|
-
else
|
|
4239
|
+
else
|
|
4048
4240
|
writeFileSync(release, 'continue');
|
|
4049
4241
|
}, 100);
|
|
4050
4242
|
}
|
|
@@ -4060,7 +4252,10 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
|
|
|
4060
4252
|
trace('target-removed'); fs.writeFileSync(${JSON.stringify(ready)}, 'ready');
|
|
4061
4253
|
if (${JSON.stringify(mode)} === 'limit') fs.writeFileSync(${JSON.stringify(join(output, 'retained'))}, 'x'.repeat(512));
|
|
4062
4254
|
while (!fs.existsSync(${JSON.stringify(release)})) await new Promise(resolve => setTimeout(resolve, 10));
|
|
4063
|
-
|
|
4255
|
+
if (${JSON.stringify(mode)} !== 'persistent') {
|
|
4256
|
+
fs.unlinkSync(${JSON.stringify(join(output, 'alias'))}); trace('alias-removed');
|
|
4257
|
+
if (${JSON.stringify(mode)} === 'chain-pass') fs.unlinkSync(${JSON.stringify(join(output, 'intermediate'))});
|
|
4258
|
+
}
|
|
4064
4259
|
process.exitCode = ${mode === 'command-error' ? 7 : 0};
|
|
4065
4260
|
})().catch(error => { console.error(error); process.exitCode = 1; });`;
|
|
4066
4261
|
try {
|
|
@@ -4071,7 +4266,7 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
|
|
|
4071
4266
|
maxNewOutputBytes: mode === 'limit' ? 256 : 4096,
|
|
4072
4267
|
outputRoots: [output],
|
|
4073
4268
|
}, controller.signal));
|
|
4074
|
-
if (mode === 'pass')
|
|
4269
|
+
if (mode === 'pass' || mode === 'chain-pass')
|
|
4075
4270
|
await run;
|
|
4076
4271
|
else
|
|
4077
4272
|
await assert.rejects(run, (error) => {
|
|
@@ -4079,7 +4274,7 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
|
|
|
4079
4274
|
assert.match(message, mode === 'cancel'
|
|
4080
4275
|
? /cancelled/u
|
|
4081
4276
|
: mode === 'persistent'
|
|
4082
|
-
? /
|
|
4277
|
+
? /broken or cyclic symbolic link.*ENOENT/u
|
|
4083
4278
|
: mode === 'limit'
|
|
4084
4279
|
? /filesystem output.*exceeded/u
|
|
4085
4280
|
: /exit 7/u);
|
|
@@ -4108,7 +4303,7 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
|
|
|
4108
4303
|
unrelatedPreserved: true,
|
|
4109
4304
|
aliasRetained: readdirSync(output).includes('alias'),
|
|
4110
4305
|
}));
|
|
4111
|
-
assert.deepEqual(readFileSync(events, 'utf8').trim().split('\n'), mode === 'cancel' || mode === 'persistent'
|
|
4306
|
+
assert.deepEqual(readFileSync(events, 'utf8').trim().split('\n'), mode === 'cancel' || mode === 'persistent' || mode === 'limit'
|
|
4112
4307
|
? ['command-started', 'target-removed']
|
|
4113
4308
|
: ['command-started', 'target-removed', 'alias-removed']);
|
|
4114
4309
|
if (mode === 'cancel' || mode === 'persistent') {
|
|
@@ -4133,12 +4328,68 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
|
|
|
4133
4328
|
rmSync(output, { recursive: true, force: true });
|
|
4134
4329
|
}
|
|
4135
4330
|
});
|
|
4136
|
-
test('injected
|
|
4331
|
+
test('injected first failed scan duration does not consume the subsequent observation stall window', async () => {
|
|
4332
|
+
const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-scan-duration-')));
|
|
4333
|
+
const { root } = await fixture();
|
|
4334
|
+
const target = join(output, 'target'), alias = join(output, 'alias'), release = join(output, 'release');
|
|
4335
|
+
writeFileSync(target, 'target');
|
|
4336
|
+
const fs = (await import('node:fs')).default;
|
|
4337
|
+
const actualRealpath = fs.realpathSync;
|
|
4338
|
+
const now = Date.now.bind(Date);
|
|
4339
|
+
let offset = 0, delayed = false, firstObservation;
|
|
4340
|
+
const clock = vi.spyOn(Date, 'now').mockImplementation(() => now() + offset);
|
|
4341
|
+
const scan = vi.spyOn(fs, 'realpathSync').mockImplementation((path, options) => {
|
|
4342
|
+
try {
|
|
4343
|
+
return actualRealpath(path, options);
|
|
4344
|
+
}
|
|
4345
|
+
catch (error) {
|
|
4346
|
+
if (path === alias && !delayed) {
|
|
4347
|
+
delayed = true;
|
|
4348
|
+
offset = 1500;
|
|
4349
|
+
}
|
|
4350
|
+
throw error;
|
|
4351
|
+
}
|
|
4352
|
+
});
|
|
4353
|
+
syncBuiltinESMExports();
|
|
4354
|
+
const progress = vi.spyOn(process.stderr, 'write').mockImplementation((line) => {
|
|
4355
|
+
if (firstObservation === undefined && String(line).includes('ENOENT')) {
|
|
4356
|
+
firstObservation = String(line);
|
|
4357
|
+
unlinkSync(alias);
|
|
4358
|
+
writeFileSync(release, 'finish');
|
|
4359
|
+
}
|
|
4360
|
+
return true;
|
|
4361
|
+
});
|
|
4362
|
+
const script = `(async () => {
|
|
4363
|
+
const fs = require('node:fs'); fs.unlinkSync(${JSON.stringify(target)});
|
|
4364
|
+
fs.symlinkSync('./target', ${JSON.stringify(alias)});
|
|
4365
|
+
while (!fs.existsSync(${JSON.stringify(release)})) await new Promise(resolve => setTimeout(resolve, 10));
|
|
4366
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });`;
|
|
4367
|
+
try {
|
|
4368
|
+
const { withRuntimeSetupWriter } = await import('./verification.js');
|
|
4369
|
+
await withRuntimeSetupWriter(root, (runner) => runner.run([process.execPath, '-e', script], {
|
|
4370
|
+
maxAggregateRssBytes: 1_000_000_000,
|
|
4371
|
+
minFreeDiskBytes: 1,
|
|
4372
|
+
maxNewOutputBytes: 4096,
|
|
4373
|
+
outputRoots: [output],
|
|
4374
|
+
}));
|
|
4375
|
+
assert.ok(delayed);
|
|
4376
|
+
assert.ok(firstObservation);
|
|
4377
|
+
assert.equal(existsSync(alias), false);
|
|
4378
|
+
}
|
|
4379
|
+
finally {
|
|
4380
|
+
progress.mockRestore();
|
|
4381
|
+
scan.mockRestore();
|
|
4382
|
+
syncBuiltinESMExports();
|
|
4383
|
+
clock.mockRestore();
|
|
4384
|
+
rmSync(root, { recursive: true, force: true });
|
|
4385
|
+
rmSync(output, { recursive: true, force: true });
|
|
4386
|
+
}
|
|
4387
|
+
});
|
|
4388
|
+
test('injected observation stall rejects a late complete retry for an unvalidated alias', async () => {
|
|
4137
4389
|
const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-observation-deadline-')));
|
|
4138
4390
|
const { root } = await fixture();
|
|
4139
4391
|
const target = join(output, 'target'), alias = join(output, 'alias'), release = join(output, 'release');
|
|
4140
4392
|
writeFileSync(target, 'target');
|
|
4141
|
-
symlinkSync('./target', alias);
|
|
4142
4393
|
const now = Date.now.bind(Date);
|
|
4143
4394
|
let offset = 0;
|
|
4144
4395
|
let firstObservation;
|
|
@@ -4158,6 +4409,7 @@ test('injected observation deadline rejects a late complete real filesystem retr
|
|
|
4158
4409
|
});
|
|
4159
4410
|
const script = `(async () => {
|
|
4160
4411
|
const fs = require('node:fs'); fs.unlinkSync(${JSON.stringify(target)});
|
|
4412
|
+
fs.symlinkSync('./target', ${JSON.stringify(alias)});
|
|
4161
4413
|
while (!fs.existsSync(${JSON.stringify(release)})) await new Promise(resolve => setTimeout(resolve, 10));
|
|
4162
4414
|
})().catch(error => { console.error(error); process.exitCode = 1; });`;
|
|
4163
4415
|
try {
|
|
@@ -4301,6 +4553,90 @@ const child = require('node:child_process').spawnSync(${JSON.stringify(consumer.
|
|
|
4301
4553
|
if (child.error) throw child.error;
|
|
4302
4554
|
process.exit(child.status ?? 1);`;
|
|
4303
4555
|
}
|
|
4556
|
+
test.each(['pass', 'absent', 'limit', 'cancel'])('Node 26 packed public verification preserves CLI rebuild %s behavior', async (mode) => {
|
|
4557
|
+
const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-packed-rebuild-')));
|
|
4558
|
+
const target = join(output, 'cli.js');
|
|
4559
|
+
writeFileSync(target, 'original CLI');
|
|
4560
|
+
symlinkSync('./cli.js', join(output, 'cli'));
|
|
4561
|
+
writeFileSync(join(output, 'unrelated'), 'preserved');
|
|
4562
|
+
const { root } = await fixture({ personalAuthor: true, firstStageScript: ordinaryCliRebuildScript(output, mode) });
|
|
4563
|
+
try {
|
|
4564
|
+
const row = await prepareIssueWorktree({
|
|
4565
|
+
baseRef: 'main',
|
|
4566
|
+
identity: 'host-author',
|
|
4567
|
+
issueNumber: 17,
|
|
4568
|
+
repoRoot: root,
|
|
4569
|
+
});
|
|
4570
|
+
const clients = syntheticDiscoveryClients();
|
|
4571
|
+
const replies = [];
|
|
4572
|
+
for (const fragment of [
|
|
4573
|
+
'query DeliveryRepository',
|
|
4574
|
+
'query ProjectDeliveryConfiguration',
|
|
4575
|
+
'issueFields(first:',
|
|
4576
|
+
'issueTypes(first:',
|
|
4577
|
+
])
|
|
4578
|
+
replies.push({ fragment, data: await clients.graphql(fragment) });
|
|
4579
|
+
const consumer = packedNode26Consumer();
|
|
4580
|
+
const result = execFileSync(consumer.executable, [
|
|
4581
|
+
'-e',
|
|
4582
|
+
`(async () => {
|
|
4583
|
+
const assert = require('node:assert/strict'), fs = require('node:fs');
|
|
4584
|
+
assert.equal(process.version, 'v26.2.0');
|
|
4585
|
+
const replies = ${JSON.stringify(replies)};
|
|
4586
|
+
// Only GitHub discovery transport is synthetic; package, writer, scanner and child filesystem are real.
|
|
4587
|
+
globalThis.fetch = async (url, options) => {
|
|
4588
|
+
assert.equal(String(url), 'https://api.github.com/graphql');
|
|
4589
|
+
const query = JSON.parse(options.body).query;
|
|
4590
|
+
const reply = replies.find(reply => query.includes(reply.fragment));
|
|
4591
|
+
assert.ok(reply, 'unexpected synthetic discovery query');
|
|
4592
|
+
return new Response(JSON.stringify({ data: reply.data }), { status: 200, headers: { 'content-type': 'application/json' } });
|
|
4593
|
+
};
|
|
4594
|
+
process.env.AUTHOR_TOKEN = 'synthetic-author-token';
|
|
4595
|
+
const { verifyIssue } = await import('@aviaratech/ai-delivery/agent');
|
|
4596
|
+
const controller = new AbortController();
|
|
4597
|
+
const observations = []; const write = process.stderr.write.bind(process.stderr);
|
|
4598
|
+
process.stderr.write = line => {
|
|
4599
|
+
observations.push(String(line));
|
|
4600
|
+
if (${JSON.stringify(mode)} === 'cancel' && String(line).includes('ENOENT')) controller.abort();
|
|
4601
|
+
return true;
|
|
4602
|
+
};
|
|
4603
|
+
try {
|
|
4604
|
+
const running = verifyIssue({ issueNumber: 17, repoRoot: ${JSON.stringify(row.path)}, signal: controller.signal,
|
|
4605
|
+
resourceBounds: { maxAggregateRssBytes: 1000000000, minFreeDiskBytes: 1,
|
|
4606
|
+
maxNewOutputBytes: ${mode === 'limit' ? 1024 : 8192}, outputRoots: [${JSON.stringify(output)}] } });
|
|
4607
|
+
if (${JSON.stringify(mode)} === 'pass') {
|
|
4608
|
+
const run = await running;
|
|
4609
|
+
assert.ok(run.resources.sampleCount > 0); assert.ok(run.resources.maxSampledNewOutputBytes > 0);
|
|
4610
|
+
assert.equal(fs.readFileSync(${JSON.stringify(target)}, 'utf8'), 'rebuilt CLI');
|
|
4611
|
+
// Compatible completed proof is reused, but the current final filesystem is still strictly scanned.
|
|
4612
|
+
fs.unlinkSync(${JSON.stringify(target)});
|
|
4613
|
+
await assert.rejects(verifyIssue({ issueNumber: 17, repoRoot: ${JSON.stringify(row.path)},
|
|
4614
|
+
resourceBounds: { maxAggregateRssBytes: 1000000000, minFreeDiskBytes: 1, maxNewOutputBytes: 8192,
|
|
4615
|
+
outputRoots: [${JSON.stringify(output)}] } }), /broken or cyclic symbolic link/);
|
|
4616
|
+
} else {
|
|
4617
|
+
await assert.rejects(running, error => {
|
|
4618
|
+
assert.match(String(error), ${mode === 'cancel' ? '/cancelled/' : mode === 'limit' ? '/filesystem output.*exceeded/' : '/broken or cyclic symbolic link/'});
|
|
4619
|
+
assert.doesNotMatch(String(error), /cleanup failed/); return true;
|
|
4620
|
+
});
|
|
4621
|
+
}
|
|
4622
|
+
assert.ok(observations.some(line => line.includes('ENOENT')));
|
|
4623
|
+
assert.equal(fs.readFileSync(${JSON.stringify(join(output, 'unrelated'))}, 'utf8'), 'preserved');
|
|
4624
|
+
console.log(JSON.stringify({ scenario: 'packed-node26-cli-rebuild', mode: ${JSON.stringify(mode)},
|
|
4625
|
+
firstObservation: observations.find(line => line.includes('ENOENT')), events: fs.readFileSync(${JSON.stringify(join(output, 'events'))}, 'utf8').trim().split('\\n') }));
|
|
4626
|
+
} finally { controller.abort(); process.stderr.write = write; }
|
|
4627
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });`,
|
|
4628
|
+
], {
|
|
4629
|
+
cwd: consumer.root,
|
|
4630
|
+
encoding: 'utf8',
|
|
4631
|
+
maxBuffer: 1024 * 1024,
|
|
4632
|
+
});
|
|
4633
|
+
console.log(result.trim());
|
|
4634
|
+
}
|
|
4635
|
+
finally {
|
|
4636
|
+
rmSync(root, { recursive: true, force: true });
|
|
4637
|
+
rmSync(output, { recursive: true, force: true });
|
|
4638
|
+
}
|
|
4639
|
+
});
|
|
4304
4640
|
function filesystemFixtureScript(output, mode, runtime = 'node24') {
|
|
4305
4641
|
const script = `
|
|
4306
4642
|
(async () => {
|