@aviaratech/ai-delivery 0.3.11 → 0.3.13

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.
@@ -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';
@@ -20,7 +21,7 @@ import * as githubClient from './github/client.js';
20
21
  import * as atomicJson from './utils/atomicJson.js';
21
22
  import { createIssue, developIssue, listIssueSubissues, readyCheck, resumeCreatedIssue, updateIssue, } from './issue.js';
22
23
  import { checkoutPr, finishIssue, listPrs, mergePr, prChecks, prInfo, publishPr, submitFormalReview } from './pr.js';
23
- import { addWorktreeEntry, removeWorktreeEntry, updateIssueWorktreeDelivery } from './services/worktreeRegistry.js';
24
+ import { addWorktreeEntry, assertNativeIssueTrackingAdmission, getIssueWorktreeStrict, removeWorktreeEntry, updateIssueWorktreeDelivery, } from './services/worktreeRegistry.js';
24
25
  import { assertDeliveryRuntimeAdmitted } from './services/deliveryAdmission.js';
25
26
  import { getDeliveryRecords } from './services/deliveryRecordService.js';
26
27
  import { createIssuePhaseEvidence, loadVerifiedRun, verifyIssue } from './verification.js';
@@ -539,38 +540,13 @@ test('an old active issue remains pinned while a new row uses the same canonical
539
540
  assert.equal(readFileSync(registryPath, 'utf8'), futureBytes);
540
541
  assert.equal(existsSync(join(root, '.worktrees', 'scratch-new')), false);
541
542
  writeFileSync(registryPath, JSON.stringify({ worktrees: [old] }));
542
- let issueWrites = 0;
543
- const relationshipContext = {
544
- root,
545
- repo: { owner: 'example', repo: 'widget' },
546
- config: (await loadDeliveryConfig(root)).config,
547
- clients: {
548
- rest: {
549
- issues: {
550
- get: async () => ({ data: { id: 17, number: 17, node_id: 'PARENT-17', state: 'open' } }),
551
- create: async () => {
552
- issueWrites += 1;
553
- return { data: {} };
554
- },
555
- update: async () => {
556
- issueWrites += 1;
557
- return { data: {} };
558
- },
559
- },
560
- },
561
- graphql: async () => ({
562
- repository: {
563
- issue: {
564
- parent: { id: 'PARENT-17', number: 17 },
565
- blockedBy: { nodes: [], pageInfo: { endCursor: null, hasNextPage: false } },
566
- },
567
- },
568
- }),
569
- },
570
- };
571
- await assert.rejects(createIssue(relationshipContext, { title: 'Synthetic child', parentIssueNumber: 17 }), /ownership witness/u);
572
- await assert.rejects(updateIssue(relationshipContext, { issueNumber: 17, parentIssueNumber: null }), /ownership witness/u);
573
- assert.equal(issueWrites, 0);
543
+ assert.doesNotThrow(() => assertNativeIssueTrackingAdmission(17, root));
544
+ const original = readFileSync(registryPath, 'utf8');
545
+ writeFileSync(registryPath, JSON.stringify({ worktrees: [old, old] }));
546
+ assert.throws(() => assertNativeIssueTrackingAdmission(17, root), /duplicate/u);
547
+ writeFileSync(registryPath, futureBytes);
548
+ assert.throws(() => assertNativeIssueTrackingAdmission(17, root), /unsupported fields/u);
549
+ writeFileSync(registryPath, original);
574
550
  await assert.rejects(prepareIssueWorktree({ identity: 'synthetic-author', issueNumber: 17, repoRoot: root }), /ownership witness/u);
575
551
  const row = await prepareStandaloneWorktree({
576
552
  branch: 'scratch/new',
@@ -732,8 +708,7 @@ test.each(['current-blocker', 'new-blocker', 'current-parent', 'new-parent'])('t
732
708
  await assert.rejects(updateIssue(context, input), /invalid blocked-by relationship evidence/u);
733
709
  assert.equal(writes, 0);
734
710
  malformedRelationships = false;
735
- await assert.rejects(updateIssue(context, { ...input, issueNumber: 17 }), /ownership witness/u);
736
- assert.equal(writes, 0);
711
+ assert.throws(() => getIssueWorktreeStrict(17, root), /ownership witness/u);
737
712
  writeFileSync(registryPath, JSON.stringify({ worktrees: [{ ...legacy, schemaVersion: 'unknown@99' }] }));
738
713
  await assert.rejects(updateIssue(context, input), /unsupported fields/u);
739
714
  assert.equal(writes, 0);
@@ -3617,7 +3592,10 @@ async function syntheticLifecycle(routing) {
3617
3592
  },
3618
3593
  }),
3619
3594
  },
3620
- repos: { getCombinedStatusForRef: async () => ({ data: { statuses: [], state: 'success' } }) },
3595
+ repos: {
3596
+ get: async () => ({ data: { full_name: 'example/widget' } }),
3597
+ getCombinedStatusForRef: async () => ({ data: { statuses: [], state: 'success' } }),
3598
+ },
3621
3599
  };
3622
3600
  let authenticatedAuthor = 'synthetic-author[bot]';
3623
3601
  const context = {
@@ -3637,7 +3615,71 @@ async function syntheticLifecycle(routing) {
3637
3615
  },
3638
3616
  };
3639
3617
  let restoreDispatchClient;
3618
+ const enableDispatch = () => {
3619
+ const dispatchClient = vi.spyOn(githubClient, 'createDeliveryGitHubClients').mockImplementation(async (input) => ({
3620
+ ...context.clients,
3621
+ ...(input.role === 'reviewer'
3622
+ ? {
3623
+ role: 'reviewer',
3624
+ appActorLogin: async () => 'synthetic-reviewer[bot]',
3625
+ }
3626
+ : {}),
3627
+ graphql: (async (query, variables) => query.includes('DeliveryDiscovery') ||
3628
+ query.includes('DeliveryRepository') ||
3629
+ query.includes('ProjectDeliveryConfiguration')
3630
+ ? syntheticDiscoveryClients().graphql(query, variables)
3631
+ : graphql(query, variables)),
3632
+ }));
3633
+ restoreDispatchClient = () => dispatchClient.mockRestore();
3634
+ return dispatchClient;
3635
+ };
3636
+ const execution = { repoRoot: root, runtimeEntryPath, identity: 'synthetic-author' };
3640
3637
  try {
3638
+ if (routing.legacyNativeMetadata) {
3639
+ enableDispatch();
3640
+ const legacyPath = join(root, '.worktrees', 'issue-17');
3641
+ git(root, 'worktree', 'add', '-b', 'issue/17', legacyPath, 'main');
3642
+ writeFileSync(join(legacyPath, 'artifact.txt'), 'unadopted legacy source\n');
3643
+ const receiptRoot = join(root, '.git', 'legacy-receipts');
3644
+ mkdirSync(receiptRoot);
3645
+ writeFileSync(join(receiptRoot, 'run.json'), '{"producer":"synthetic-legacy"}\n');
3646
+ const registryPath = join(root, '.issue-cli', 'worktrees.json');
3647
+ mkdirSync(dirname(registryPath), { recursive: true });
3648
+ const legacy = {
3649
+ branch: 'issue/17',
3650
+ createdAt: '2026-01-01T00:00:00.000Z',
3651
+ identity: 'synthetic-legacy',
3652
+ issueNumber,
3653
+ path: legacyPath,
3654
+ status: 'active',
3655
+ type: 'issue',
3656
+ updatedAt: '2026-01-01T00:00:00.000Z',
3657
+ };
3658
+ const registryBytes = JSON.stringify({ worktrees: [legacy] });
3659
+ writeFileSync(registryPath, registryBytes);
3660
+ const source = directoryHash(legacyPath);
3661
+ const receipts = directoryHash(receiptRoot);
3662
+ await assert.rejects(executeTool('issue_update', { issueNumber, points: 2 }, {
3663
+ ...execution,
3664
+ identity: 'synthetic-reviewer',
3665
+ }), /identity|author|permitted|allowed/u);
3666
+ const updated = (await executeTool('issue_update', {
3667
+ issueNumber,
3668
+ parentIssueNumber: 11,
3669
+ points: 2,
3670
+ priority: 'High',
3671
+ }, execution));
3672
+ assert.equal(updated.points, 2);
3673
+ assert.equal(updated.priority, 'High');
3674
+ assert.equal(parentIssueNumber, 11);
3675
+ assert.equal(parentPoints, undefined);
3676
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /ownership witness/u);
3677
+ assert.equal(readFileSync(registryPath, 'utf8'), registryBytes);
3678
+ assert.equal(directoryHash(legacyPath), source);
3679
+ assert.equal(directoryHash(receiptRoot), receipts);
3680
+ assert.equal(existsSync(join(gitCommonDir(root), 'ai-delivery/worktree-owners')), false);
3681
+ return;
3682
+ }
3641
3683
  if (routing.producerOnboarding) {
3642
3684
  assert.equal(existsSync(join(root, 'AGENTS.md')), false);
3643
3685
  const archiveBase = join(root, '.git', 'producer-fixture');
@@ -3941,21 +3983,126 @@ exec "${realGit}" "$@"
3941
3983
  await assert.rejects(finishIssue(context, { issueNumber, prNumber, strategy: 'merge' }), /exact prepared merge intent/u);
3942
3984
  headSha = reviewedHead;
3943
3985
  assert.equal(calls.filter((call) => call === 'git:createCommit').length, 1);
3986
+ if (routing.mergedContinuation) {
3987
+ enableDispatch();
3988
+ const terminal = await executeTool('issue_pr_merge', { issueNumber, prNumber, strategy: 'merge' }, execution);
3989
+ assert.equal(terminal.mergeSha, mergeSha);
3990
+ assert.equal(issue.state, 'open');
3991
+ const previous = getIssueWorktreeStrict(issueNumber, root);
3992
+ assert.equal(previous.status, 'merged');
3993
+ const registryPath = join(root, '.issue-cli', 'worktrees.json');
3994
+ const registryBytes = readFileSync(registryPath);
3995
+ const custody = [
3996
+ 'runs@2',
3997
+ 'publications',
3998
+ 'reviews',
3999
+ 'merge-intents',
4000
+ 'merge-attempts',
4001
+ 'merge-results',
4002
+ 'merges',
4003
+ ];
4004
+ const oldReceipts = [];
4005
+ const saveReceipts = (path) => {
4006
+ if (!existsSync(path))
4007
+ return;
4008
+ for (const entry of readdirSync(path, { withFileTypes: true })) {
4009
+ const child = join(path, entry.name);
4010
+ if (entry.isDirectory())
4011
+ saveReceipts(child);
4012
+ else
4013
+ oldReceipts.push({ path: child, bytes: readFileSync(child) });
4014
+ }
4015
+ };
4016
+ for (const name of custody)
4017
+ saveReceipts(join(gitCommonDir(root), 'ai-delivery', name));
4018
+ const ownerPath = join(gitCommonDir(root), 'ai-delivery/worktree-owners');
4019
+ const ownerHash = directoryHash(ownerPath);
4020
+ const publicationPath = join(gitCommonDir(root), 'ai-delivery/publications', String(issueNumber), `${headSha}.json`);
4021
+ const publicationBytes = readFileSync(publicationPath);
4022
+ const { publicationId: _publicationId, ...publicationContent } = JSON.parse(publicationBytes.toString());
4023
+ for (const stale of [
4024
+ { issueNumber: 18 },
4025
+ { headSha: 'c'.repeat(40) },
4026
+ { baseSha: 'c'.repeat(40) },
4027
+ { evidenceId: `sha256:${'c'.repeat(64)}` },
4028
+ ]) {
4029
+ const content = { ...publicationContent, ...stale };
4030
+ const bytes = Buffer.from(JSON.stringify({ ...content, publicationId: digestValue(content) }));
4031
+ writeFileSync(publicationPath, bytes, { mode: 0o600 });
4032
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /publication.*(?:verified coordinates|historical approved review)/u);
4033
+ assert.deepEqual(readFileSync(registryPath), registryBytes);
4034
+ assert.deepEqual(readFileSync(publicationPath), bytes);
4035
+ }
4036
+ writeFileSync(publicationPath, publicationBytes, { mode: 0o600 });
4037
+ const reviewPath = join(gitCommonDir(root), 'ai-delivery/reviews', String(issueNumber), `${headSha}.json`);
4038
+ const reviewBytes = readFileSync(reviewPath);
4039
+ const { receiptId: _receiptId, artifact: savedArtifact, ...reviewContent } = JSON.parse(reviewBytes.toString());
4040
+ const { artifactId: _artifactId, ...artifactContent } = savedArtifact;
4041
+ const staleArtifact = { ...artifactContent, diffScopeHash: `sha256:${'c'.repeat(64)}` };
4042
+ const staleReview = { ...reviewContent, artifact: { ...staleArtifact, artifactId: digestValue(staleArtifact) } };
4043
+ const staleReviewBytes = Buffer.from(JSON.stringify({ ...staleReview, receiptId: digestValue(staleReview) }));
4044
+ writeFileSync(reviewPath, staleReviewBytes, { mode: 0o600 });
4045
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /exact historical approved review/u);
4046
+ assert.deepEqual(readFileSync(registryPath), registryBytes);
4047
+ assert.deepEqual(readFileSync(reviewPath), staleReviewBytes);
4048
+ writeFileSync(reviewPath, reviewBytes, { mode: 0o600 });
4049
+ await assert.rejects(developIssue({
4050
+ ...context,
4051
+ config: {
4052
+ ...config,
4053
+ roles: {
4054
+ ...config.roles,
4055
+ author: { ...config.roles.author, identity: 'other-owner' },
4056
+ },
4057
+ },
4058
+ }, issueNumber), /same preparing owner/u);
4059
+ issue.state = 'closed';
4060
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /closed/u);
4061
+ issue.state = 'open';
4062
+ headSha = 'b'.repeat(40);
4063
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /terminal receipt/u);
4064
+ headSha = reviewedHead;
4065
+ const mergePath = join(gitCommonDir(root), 'ai-delivery/merges', String(issueNumber), `${headSha}.json`);
4066
+ const mergeBytes = readFileSync(mergePath);
4067
+ unlinkSync(mergePath);
4068
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /exact terminal merge receipt/u);
4069
+ writeFileSync(mergePath, mergeBytes, { mode: 0o600 });
4070
+ assert.deepEqual(readFileSync(registryPath), registryBytes);
4071
+ projectStatus = null;
4072
+ failNextProjectSync = true;
4073
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /synthetic Project interruption/u);
4074
+ const reactivated = getIssueWorktreeStrict(issueNumber, root);
4075
+ assert.equal(reactivated.status, 'active');
4076
+ assert.equal(reactivated.prNumber, undefined);
4077
+ assert.equal(reactivated.identity, previous.identity);
4078
+ assert.equal(reactivated.createdAt, previous.createdAt);
4079
+ const resumed = await executeTool('issue_develop', { issueNumber }, execution);
4080
+ assert.deepEqual(resumed, { path: previous.path, branch: previous.branch });
4081
+ assert.equal(projectStatus, 'Active');
4082
+ await assert.rejects(verifyIssue({ issueNumber, repoRoot: row.path }), /already-merged issue head/u);
4083
+ for (const saved of oldReceipts) {
4084
+ assert.deepEqual(readFileSync(saved.path), saved.bytes);
4085
+ }
4086
+ assert.equal(directoryHash(ownerPath), ownerHash);
4087
+ // A fresh commit produces new verification slots while the complete prior head remains immutable.
4088
+ writeFileSync(join(row.path, 'change.txt'), 'continued feature\n');
4089
+ git(row.path, 'add', 'change.txt');
4090
+ git(row.path, 'commit', '-qm', 'continue unfinished synthetic issue');
4091
+ const continued = await verifyIssue({ issueNumber, repoRoot: row.path });
4092
+ assert.notEqual(continued.classification.head.sha, reviewedHead);
4093
+ for (const saved of oldReceipts)
4094
+ assert.deepEqual(readFileSync(saved.path), saved.bytes);
4095
+ assert.equal(directoryHash(ownerPath), ownerHash);
4096
+ assert.equal(calls.filter((call) => call === 'git:createCommit').length, 1);
4097
+ return;
4098
+ }
3944
4099
  const blockedRecordPath = join(gitCommonDir(root), 'ai-delivery', 'deliveries.json');
3945
4100
  mkdirSync(blockedRecordPath);
3946
4101
  await assert.rejects(finishIssue(context, { issueNumber, prNumber, strategy: 'merge' }), /Delivery record store is invalid/u);
3947
4102
  assert.equal(existsSync(row.path), false);
3948
4103
  rmSync(blockedRecordPath, { recursive: true });
3949
- const dispatchClient = vi.spyOn(githubClient, 'createDeliveryGitHubClients').mockImplementation(async () => ({
3950
- ...context.clients,
3951
- graphql: (async (query, variables) => query.includes('DeliveryDiscovery') ||
3952
- query.includes('DeliveryRepository') ||
3953
- query.includes('ProjectDeliveryConfiguration')
3954
- ? syntheticDiscoveryClients().graphql(query, variables)
3955
- : graphql(query, variables)),
3956
- }));
3957
- restoreDispatchClient = () => dispatchClient.mockRestore();
3958
- const execution = { repoRoot: root, runtimeEntryPath, identity: 'synthetic-author' };
4104
+ const dispatchClient = enableDispatch();
4105
+ execution.runtimeEntryPath = runtimeEntryPath;
3959
4106
  const terminalMerge = (await executeTool('issue_pr_merge', { issueNumber, prNumber, strategy: 'merge' }, execution));
3960
4107
  assert.equal(terminalMerge.mergeSha, git(remote, 'rev-parse', 'refs/heads/main'));
3961
4108
  const finished = (await executeTool('issue_finish', { issueNumber, prNumber, strategy: 'merge' }, execution));
@@ -4019,10 +4166,201 @@ test.each([
4019
4166
  { remote: 'upstream', divergentOrigin: false },
4020
4167
  { remote: 'upstream', divergentOrigin: true },
4021
4168
  ])('synthetic lifecycle finishes with remote $remote and divergent origin $divergentOrigin', syntheticLifecycle, 15_000);
4169
+ test('supported develop resumes an open merged issue under the same owner and preserves prior receipts', { timeout: 0 }, async () => syntheticLifecycle({ remote: 'origin', divergentOrigin: false, mergedContinuation: true }));
4170
+ test('supported native metadata updates a legacy target without adopting its source or receipts', { timeout: 0 }, async () => syntheticLifecycle({ remote: 'origin', divergentOrigin: false, legacyNativeMetadata: true }));
4022
4171
  test('an unrelated consumer uses public MCP setup through verified configured-author publication, counted review readback, merge and cleanup', { timeout: 0 }, async () => {
4023
4172
  await syntheticLifecycle({ remote: 'origin', divergentOrigin: false, producerOnboarding: true });
4024
4173
  });
4025
- test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary owned symlink teardown preserves observation and %s command evidence', async (mode) => {
4174
+ function ordinaryCliRebuildScript(output, mode = 'pass', target = join(output, 'cli.js')) {
4175
+ return `(async () => {
4176
+ const fs = require('node:fs');
4177
+ const trace = event => fs.appendFileSync(${JSON.stringify(join(output, 'events'))}, event + '\\n');
4178
+ trace('command-started'); fs.unlinkSync(${JSON.stringify(target)}); trace('target-removed');
4179
+ if (${JSON.stringify(dirname(target))} !== ${JSON.stringify(output)}) fs.rmdirSync(${JSON.stringify(dirname(target))});
4180
+ if (${JSON.stringify(mode)} === 'limit') fs.writeFileSync(${JSON.stringify(join(output, 'oversized'))}, 'x'.repeat(4096));
4181
+ for (let unit = 0; unit < 32; unit++) {
4182
+ fs.appendFileSync(${JSON.stringify(join(output, 'compiled'))}, 'build-unit\\n');
4183
+ trace('build-unit-' + unit);
4184
+ await new Promise(resolve => setTimeout(resolve, 100));
4185
+ }
4186
+ if (${JSON.stringify(mode)} !== 'absent') {
4187
+ fs.mkdirSync(${JSON.stringify(dirname(target))}, { recursive: true });
4188
+ fs.writeFileSync(${JSON.stringify(target)}, 'rebuilt CLI'); trace('target-restored');
4189
+ }
4190
+ })().catch(error => { console.error(error); process.exitCode = 1; });`;
4191
+ }
4192
+ test.each(['direct', 'chain', 'directory'])('ordinary bounded rebuild restores its %s CLI target after observable build work', async (shape) => {
4193
+ const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-cli-rebuild-')));
4194
+ const { root } = await fixture();
4195
+ const target = shape === 'directory' ? join(output, 'dist', 'cli.js') : join(output, 'cli.js');
4196
+ const alias = join(output, 'cli');
4197
+ const events = join(output, 'events');
4198
+ mkdirSync(dirname(target), { recursive: true });
4199
+ writeFileSync(target, 'original CLI');
4200
+ writeFileSync(join(output, 'unrelated'), 'preserved');
4201
+ if (shape === 'direct')
4202
+ symlinkSync('./cli.js', alias);
4203
+ else {
4204
+ symlinkSync(shape === 'chain' ? './cli.js' : './dist', join(output, 'intermediate'));
4205
+ symlinkSync(shape === 'chain' ? './intermediate' : './intermediate/cli.js', alias);
4206
+ }
4207
+ const observations = [];
4208
+ const duringAbsence = [];
4209
+ const progress = vi.spyOn(process.stderr, 'write').mockImplementation((line) => {
4210
+ observations.push(String(line));
4211
+ if (!existsSync(target) && String(line).includes('sampledNewOutputBytes'))
4212
+ duringAbsence.push(JSON.parse(String(line).slice(String(line).indexOf('{'))));
4213
+ return true;
4214
+ });
4215
+ const script = ordinaryCliRebuildScript(output, 'pass', target);
4216
+ try {
4217
+ const { withRuntimeSetupWriter } = await import('./verification.js');
4218
+ await withRuntimeSetupWriter(root, (runner) => runner.run([process.execPath, '-e', script], {
4219
+ maxAggregateRssBytes: 1_000_000_000,
4220
+ minFreeDiskBytes: 1,
4221
+ maxNewOutputBytes: 8192,
4222
+ outputRoots: [output],
4223
+ }));
4224
+ assert.equal(readFileSync(target, 'utf8'), 'rebuilt CLI');
4225
+ assert.equal(readFileSync(join(output, 'compiled'), 'utf8').split('\n').length, 33);
4226
+ assert.equal(readFileSync(join(output, 'unrelated'), 'utf8'), 'preserved');
4227
+ assert.ok(duringAbsence.some((sample) => (sample.sampledNewOutputBytes ?? 0) > 0));
4228
+ assert.ok(duringAbsence.every((sample) => (sample.sampledAggregateRssBytes ?? 0) > 0));
4229
+ }
4230
+ finally {
4231
+ console.log(JSON.stringify({
4232
+ scenario: 'ordinary-cli-rebuild',
4233
+ shape,
4234
+ firstObservation: observations.find((line) => line.includes('ENOENT')),
4235
+ events: readFileSync(events, 'utf8').trim().split('\n'),
4236
+ targetRestored: existsSync(target),
4237
+ unrelatedPreserved: readFileSync(join(output, 'unrelated'), 'utf8') === 'preserved',
4238
+ }));
4239
+ progress.mockRestore();
4240
+ rmSync(root, { recursive: true, force: true });
4241
+ rmSync(output, { recursive: true, force: true });
4242
+ }
4243
+ });
4244
+ test.each([
4245
+ 'bytes',
4246
+ 'rss',
4247
+ 'disk',
4248
+ 'fifo',
4249
+ 'permission',
4250
+ 'identity',
4251
+ 'ancestor',
4252
+ 'cycle',
4253
+ 'chain-identity',
4254
+ 'directory-identity',
4255
+ 'chain-cycle',
4256
+ ])('a missing validated target retains complete scan and %s rejection across roots', async (mode) => {
4257
+ const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-rebuild-boundary-')));
4258
+ const { root } = await fixture();
4259
+ const first = join(output, 'first'), second = join(output, 'second');
4260
+ mkdirSync(first);
4261
+ mkdirSync(second);
4262
+ const directory = join(first, 'dist'), target = join(directory, 'cli.js'), alias = join(first, 'cli');
4263
+ mkdirSync(directory);
4264
+ writeFileSync(target, 'CLI');
4265
+ const intermediate = join(first, 'intermediate');
4266
+ if (mode === 'chain-identity' || mode === 'chain-cycle') {
4267
+ symlinkSync('./dist/cli.js', intermediate);
4268
+ symlinkSync('./intermediate', alias);
4269
+ }
4270
+ else if (mode === 'directory-identity') {
4271
+ symlinkSync('./dist', intermediate);
4272
+ symlinkSync('./intermediate/cli.js', alias);
4273
+ }
4274
+ else
4275
+ symlinkSync('./dist/cli.js', alias);
4276
+ writeFileSync(join(second, 'unrelated'), 'preserved');
4277
+ const released = join(second, 'released');
4278
+ const observations = [];
4279
+ const progress = vi.spyOn(process.stderr, 'write').mockImplementation((line) => {
4280
+ observations.push(String(line));
4281
+ if (String(line).includes('ENOENT'))
4282
+ writeFileSync(released, 'observed');
4283
+ return true;
4284
+ });
4285
+ const script = `(async () => {
4286
+ const fs = require('node:fs'), cp = require('node:child_process');
4287
+ fs.unlinkSync(${JSON.stringify(target)});
4288
+ while (!fs.existsSync(${JSON.stringify(released)})) await new Promise(resolve => setTimeout(resolve, 10));
4289
+ if (${JSON.stringify(mode)} === 'bytes') fs.writeFileSync(${JSON.stringify(join(second, 'oversized'))}, 'x'.repeat(4096));
4290
+ if (${JSON.stringify(mode)} === 'rss') globalThis.retainedBuildMemory = Buffer.alloc(200 * 1024 * 1024, 1);
4291
+ if (${JSON.stringify(mode)} === 'disk') fs.writeFileSync(${JSON.stringify(join(second, 'disk-output'))}, Buffer.alloc(64 * 1024 * 1024, 1));
4292
+ if (${JSON.stringify(mode)} === 'fifo') cp.execFileSync('mkfifo', [${JSON.stringify(join(second, 'unsupported'))}]);
4293
+ if (${JSON.stringify(mode)} === 'permission') {
4294
+ fs.mkdirSync(${JSON.stringify(join(second, 'denied'))});
4295
+ fs.chmodSync(${JSON.stringify(join(second, 'denied'))}, 0);
4296
+ }
4297
+ if (${JSON.stringify(mode)} === 'identity') {
4298
+ fs.unlinkSync(${JSON.stringify(alias)}); fs.writeFileSync(${JSON.stringify(join(first, 'other'))}, 'other');
4299
+ fs.symlinkSync('./other', ${JSON.stringify(alias)});
4300
+ }
4301
+ if (${JSON.stringify(mode)} === 'ancestor') {
4302
+ fs.rmdirSync(${JSON.stringify(directory)}); fs.symlinkSync(${JSON.stringify(second)}, ${JSON.stringify(directory)});
4303
+ }
4304
+ if (${JSON.stringify(mode)} === 'cycle') fs.symlinkSync('./cli.js', ${JSON.stringify(target)});
4305
+ if (${JSON.stringify(mode)} === 'chain-identity') {
4306
+ fs.unlinkSync(${JSON.stringify(intermediate)}); fs.writeFileSync(${JSON.stringify(join(first, 'other'))}, 'other');
4307
+ fs.symlinkSync('./other', ${JSON.stringify(intermediate)});
4308
+ }
4309
+ if (${JSON.stringify(mode)} === 'directory-identity') {
4310
+ fs.unlinkSync(${JSON.stringify(intermediate)}); fs.mkdirSync(${JSON.stringify(join(first, 'other'))});
4311
+ fs.writeFileSync(${JSON.stringify(join(first, 'other', 'cli.js'))}, 'other');
4312
+ fs.symlinkSync('./other', ${JSON.stringify(intermediate)});
4313
+ }
4314
+ if (${JSON.stringify(mode)} === 'chain-cycle') {
4315
+ fs.unlinkSync(${JSON.stringify(intermediate)}); fs.symlinkSync('./cli', ${JSON.stringify(intermediate)});
4316
+ }
4317
+ for (let unit = 0; unit < 32; unit++) {
4318
+ fs.appendFileSync(${JSON.stringify(join(second, 'work'))}, 'unit\\n');
4319
+ await new Promise(resolve => setTimeout(resolve, 100));
4320
+ }
4321
+ fs.writeFileSync(${JSON.stringify(target)}, 'rebuilt CLI');
4322
+ })().catch(error => { console.error(error); process.exitCode = 1; });`;
4323
+ try {
4324
+ const { withRuntimeSetupWriter } = await import('./verification.js');
4325
+ const disk = (await import('node:fs')).statfsSync(output);
4326
+ await assert.rejects(withRuntimeSetupWriter(root, (runner) => runner.run([process.execPath, '-e', script], {
4327
+ maxAggregateRssBytes: mode === 'rss' ? 128 * 1024 * 1024 : 1_000_000_000,
4328
+ minFreeDiskBytes: mode === 'disk' ? disk.bavail * disk.bsize - 32 * 1024 * 1024 : 1,
4329
+ maxNewOutputBytes: mode === 'disk' ? 128 * 1024 * 1024 : 1024,
4330
+ outputRoots: [first, second],
4331
+ })), (error) => {
4332
+ const message = String(error);
4333
+ assert.match(message, mode === 'bytes'
4334
+ ? /filesystem output.*exceeded/u
4335
+ : mode === 'rss'
4336
+ ? /aggregate RSS.*exceeded/u
4337
+ : mode === 'disk'
4338
+ ? /Free disk fell below/u
4339
+ : mode === 'fifo'
4340
+ ? /unsupported file/u
4341
+ : mode === 'permission'
4342
+ ? /readdir failed.*EACCES/u
4343
+ : mode === 'cycle' || mode === 'chain-cycle'
4344
+ ? /ELOOP/u
4345
+ : /identity changed/u);
4346
+ assert.match(message, /First filesystem observation:.*ENOENT.*Last filesystem observation:/u);
4347
+ assert.doesNotMatch(message, /cleanup failed/u);
4348
+ return true;
4349
+ });
4350
+ assert.ok(observations.some((line) => line.includes('ENOENT')));
4351
+ assert.equal(observations.some((line) => line.includes('"state":"completed"')), false);
4352
+ assert.equal(readFileSync(join(second, 'unrelated'), 'utf8'), 'preserved');
4353
+ }
4354
+ finally {
4355
+ const denied = join(second, 'denied');
4356
+ if (existsSync(denied))
4357
+ chmodSync(denied, 0o700);
4358
+ progress.mockRestore();
4359
+ rmSync(root, { recursive: true, force: true });
4360
+ rmSync(output, { recursive: true, force: true });
4361
+ }
4362
+ });
4363
+ test.each(['pass', 'chain-pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary owned symlink teardown preserves observation and %s command evidence', async (mode) => {
4026
4364
  const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-ordinary-teardown-')));
4027
4365
  const { root } = await fixture();
4028
4366
  const controller = new AbortController();
@@ -4031,7 +4369,9 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
4031
4369
  const release = join(output, 'release');
4032
4370
  writeFileSync(join(output, 'unrelated'), 'preserved');
4033
4371
  writeFileSync(join(output, 'target'), 'x'.repeat(128));
4034
- symlinkSync('./target', join(output, 'alias'));
4372
+ if (mode === 'chain-pass')
4373
+ symlinkSync('./target', join(output, 'intermediate'));
4374
+ symlinkSync(mode === 'chain-pass' ? './intermediate' : './target', join(output, 'alias'));
4035
4375
  let observedGap = false;
4036
4376
  const observations = [];
4037
4377
  let releaseTimer;
@@ -4044,7 +4384,7 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
4044
4384
  releaseTimer = setTimeout(() => {
4045
4385
  if (mode === 'cancel')
4046
4386
  controller.abort();
4047
- else if (mode !== 'persistent')
4387
+ else
4048
4388
  writeFileSync(release, 'continue');
4049
4389
  }, 100);
4050
4390
  }
@@ -4060,7 +4400,10 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
4060
4400
  trace('target-removed'); fs.writeFileSync(${JSON.stringify(ready)}, 'ready');
4061
4401
  if (${JSON.stringify(mode)} === 'limit') fs.writeFileSync(${JSON.stringify(join(output, 'retained'))}, 'x'.repeat(512));
4062
4402
  while (!fs.existsSync(${JSON.stringify(release)})) await new Promise(resolve => setTimeout(resolve, 10));
4063
- fs.unlinkSync(${JSON.stringify(join(output, 'alias'))}); trace('alias-removed');
4403
+ if (${JSON.stringify(mode)} !== 'persistent') {
4404
+ fs.unlinkSync(${JSON.stringify(join(output, 'alias'))}); trace('alias-removed');
4405
+ if (${JSON.stringify(mode)} === 'chain-pass') fs.unlinkSync(${JSON.stringify(join(output, 'intermediate'))});
4406
+ }
4064
4407
  process.exitCode = ${mode === 'command-error' ? 7 : 0};
4065
4408
  })().catch(error => { console.error(error); process.exitCode = 1; });`;
4066
4409
  try {
@@ -4071,7 +4414,7 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
4071
4414
  maxNewOutputBytes: mode === 'limit' ? 256 : 4096,
4072
4415
  outputRoots: [output],
4073
4416
  }, controller.signal));
4074
- if (mode === 'pass')
4417
+ if (mode === 'pass' || mode === 'chain-pass')
4075
4418
  await run;
4076
4419
  else
4077
4420
  await assert.rejects(run, (error) => {
@@ -4079,7 +4422,7 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
4079
4422
  assert.match(message, mode === 'cancel'
4080
4423
  ? /cancelled/u
4081
4424
  : mode === 'persistent'
4082
- ? /remained unavailable.*First observation:.*ENOENT/u
4425
+ ? /broken or cyclic symbolic link.*ENOENT/u
4083
4426
  : mode === 'limit'
4084
4427
  ? /filesystem output.*exceeded/u
4085
4428
  : /exit 7/u);
@@ -4108,7 +4451,7 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
4108
4451
  unrelatedPreserved: true,
4109
4452
  aliasRetained: readdirSync(output).includes('alias'),
4110
4453
  }));
4111
- assert.deepEqual(readFileSync(events, 'utf8').trim().split('\n'), mode === 'cancel' || mode === 'persistent'
4454
+ assert.deepEqual(readFileSync(events, 'utf8').trim().split('\n'), mode === 'cancel' || mode === 'persistent' || mode === 'limit'
4112
4455
  ? ['command-started', 'target-removed']
4113
4456
  : ['command-started', 'target-removed', 'alias-removed']);
4114
4457
  if (mode === 'cancel' || mode === 'persistent') {
@@ -4133,12 +4476,68 @@ test.each(['pass', 'command-error', 'cancel', 'persistent', 'limit'])('ordinary
4133
4476
  rmSync(output, { recursive: true, force: true });
4134
4477
  }
4135
4478
  });
4136
- test('injected observation deadline rejects a late complete real filesystem retry', async () => {
4479
+ test('injected first failed scan duration does not consume the subsequent observation stall window', async () => {
4480
+ const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-scan-duration-')));
4481
+ const { root } = await fixture();
4482
+ const target = join(output, 'target'), alias = join(output, 'alias'), release = join(output, 'release');
4483
+ writeFileSync(target, 'target');
4484
+ const fs = (await import('node:fs')).default;
4485
+ const actualRealpath = fs.realpathSync;
4486
+ const now = Date.now.bind(Date);
4487
+ let offset = 0, delayed = false, firstObservation;
4488
+ const clock = vi.spyOn(Date, 'now').mockImplementation(() => now() + offset);
4489
+ const scan = vi.spyOn(fs, 'realpathSync').mockImplementation((path, options) => {
4490
+ try {
4491
+ return actualRealpath(path, options);
4492
+ }
4493
+ catch (error) {
4494
+ if (path === alias && !delayed) {
4495
+ delayed = true;
4496
+ offset = 1500;
4497
+ }
4498
+ throw error;
4499
+ }
4500
+ });
4501
+ syncBuiltinESMExports();
4502
+ const progress = vi.spyOn(process.stderr, 'write').mockImplementation((line) => {
4503
+ if (firstObservation === undefined && String(line).includes('ENOENT')) {
4504
+ firstObservation = String(line);
4505
+ unlinkSync(alias);
4506
+ writeFileSync(release, 'finish');
4507
+ }
4508
+ return true;
4509
+ });
4510
+ const script = `(async () => {
4511
+ const fs = require('node:fs'); fs.unlinkSync(${JSON.stringify(target)});
4512
+ fs.symlinkSync('./target', ${JSON.stringify(alias)});
4513
+ while (!fs.existsSync(${JSON.stringify(release)})) await new Promise(resolve => setTimeout(resolve, 10));
4514
+ })().catch(error => { console.error(error); process.exitCode = 1; });`;
4515
+ try {
4516
+ const { withRuntimeSetupWriter } = await import('./verification.js');
4517
+ await withRuntimeSetupWriter(root, (runner) => runner.run([process.execPath, '-e', script], {
4518
+ maxAggregateRssBytes: 1_000_000_000,
4519
+ minFreeDiskBytes: 1,
4520
+ maxNewOutputBytes: 4096,
4521
+ outputRoots: [output],
4522
+ }));
4523
+ assert.ok(delayed);
4524
+ assert.ok(firstObservation);
4525
+ assert.equal(existsSync(alias), false);
4526
+ }
4527
+ finally {
4528
+ progress.mockRestore();
4529
+ scan.mockRestore();
4530
+ syncBuiltinESMExports();
4531
+ clock.mockRestore();
4532
+ rmSync(root, { recursive: true, force: true });
4533
+ rmSync(output, { recursive: true, force: true });
4534
+ }
4535
+ });
4536
+ test('injected observation stall rejects a late complete retry for an unvalidated alias', async () => {
4137
4537
  const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-observation-deadline-')));
4138
4538
  const { root } = await fixture();
4139
4539
  const target = join(output, 'target'), alias = join(output, 'alias'), release = join(output, 'release');
4140
4540
  writeFileSync(target, 'target');
4141
- symlinkSync('./target', alias);
4142
4541
  const now = Date.now.bind(Date);
4143
4542
  let offset = 0;
4144
4543
  let firstObservation;
@@ -4158,6 +4557,7 @@ test('injected observation deadline rejects a late complete real filesystem retr
4158
4557
  });
4159
4558
  const script = `(async () => {
4160
4559
  const fs = require('node:fs'); fs.unlinkSync(${JSON.stringify(target)});
4560
+ fs.symlinkSync('./target', ${JSON.stringify(alias)});
4161
4561
  while (!fs.existsSync(${JSON.stringify(release)})) await new Promise(resolve => setTimeout(resolve, 10));
4162
4562
  })().catch(error => { console.error(error); process.exitCode = 1; });`;
4163
4563
  try {
@@ -4301,6 +4701,90 @@ const child = require('node:child_process').spawnSync(${JSON.stringify(consumer.
4301
4701
  if (child.error) throw child.error;
4302
4702
  process.exit(child.status ?? 1);`;
4303
4703
  }
4704
+ test.each(['pass', 'absent', 'limit', 'cancel'])('Node 26 packed public verification preserves CLI rebuild %s behavior', async (mode) => {
4705
+ const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-packed-rebuild-')));
4706
+ const target = join(output, 'cli.js');
4707
+ writeFileSync(target, 'original CLI');
4708
+ symlinkSync('./cli.js', join(output, 'cli'));
4709
+ writeFileSync(join(output, 'unrelated'), 'preserved');
4710
+ const { root } = await fixture({ personalAuthor: true, firstStageScript: ordinaryCliRebuildScript(output, mode) });
4711
+ try {
4712
+ const row = await prepareIssueWorktree({
4713
+ baseRef: 'main',
4714
+ identity: 'host-author',
4715
+ issueNumber: 17,
4716
+ repoRoot: root,
4717
+ });
4718
+ const clients = syntheticDiscoveryClients();
4719
+ const replies = [];
4720
+ for (const fragment of [
4721
+ 'query DeliveryRepository',
4722
+ 'query ProjectDeliveryConfiguration',
4723
+ 'issueFields(first:',
4724
+ 'issueTypes(first:',
4725
+ ])
4726
+ replies.push({ fragment, data: await clients.graphql(fragment) });
4727
+ const consumer = packedNode26Consumer();
4728
+ const result = execFileSync(consumer.executable, [
4729
+ '-e',
4730
+ `(async () => {
4731
+ const assert = require('node:assert/strict'), fs = require('node:fs');
4732
+ assert.equal(process.version, 'v26.2.0');
4733
+ const replies = ${JSON.stringify(replies)};
4734
+ // Only GitHub discovery transport is synthetic; package, writer, scanner and child filesystem are real.
4735
+ globalThis.fetch = async (url, options) => {
4736
+ assert.equal(String(url), 'https://api.github.com/graphql');
4737
+ const query = JSON.parse(options.body).query;
4738
+ const reply = replies.find(reply => query.includes(reply.fragment));
4739
+ assert.ok(reply, 'unexpected synthetic discovery query');
4740
+ return new Response(JSON.stringify({ data: reply.data }), { status: 200, headers: { 'content-type': 'application/json' } });
4741
+ };
4742
+ process.env.AUTHOR_TOKEN = 'synthetic-author-token';
4743
+ const { verifyIssue } = await import('@aviaratech/ai-delivery/agent');
4744
+ const controller = new AbortController();
4745
+ const observations = []; const write = process.stderr.write.bind(process.stderr);
4746
+ process.stderr.write = line => {
4747
+ observations.push(String(line));
4748
+ if (${JSON.stringify(mode)} === 'cancel' && String(line).includes('ENOENT')) controller.abort();
4749
+ return true;
4750
+ };
4751
+ try {
4752
+ const running = verifyIssue({ issueNumber: 17, repoRoot: ${JSON.stringify(row.path)}, signal: controller.signal,
4753
+ resourceBounds: { maxAggregateRssBytes: 1000000000, minFreeDiskBytes: 1,
4754
+ maxNewOutputBytes: ${mode === 'limit' ? 1024 : 8192}, outputRoots: [${JSON.stringify(output)}] } });
4755
+ if (${JSON.stringify(mode)} === 'pass') {
4756
+ const run = await running;
4757
+ assert.ok(run.resources.sampleCount > 0); assert.ok(run.resources.maxSampledNewOutputBytes > 0);
4758
+ assert.equal(fs.readFileSync(${JSON.stringify(target)}, 'utf8'), 'rebuilt CLI');
4759
+ // Compatible completed proof is reused, but the current final filesystem is still strictly scanned.
4760
+ fs.unlinkSync(${JSON.stringify(target)});
4761
+ await assert.rejects(verifyIssue({ issueNumber: 17, repoRoot: ${JSON.stringify(row.path)},
4762
+ resourceBounds: { maxAggregateRssBytes: 1000000000, minFreeDiskBytes: 1, maxNewOutputBytes: 8192,
4763
+ outputRoots: [${JSON.stringify(output)}] } }), /broken or cyclic symbolic link/);
4764
+ } else {
4765
+ await assert.rejects(running, error => {
4766
+ assert.match(String(error), ${mode === 'cancel' ? '/cancelled/' : mode === 'limit' ? '/filesystem output.*exceeded/' : '/broken or cyclic symbolic link/'});
4767
+ assert.doesNotMatch(String(error), /cleanup failed/); return true;
4768
+ });
4769
+ }
4770
+ assert.ok(observations.some(line => line.includes('ENOENT')));
4771
+ assert.equal(fs.readFileSync(${JSON.stringify(join(output, 'unrelated'))}, 'utf8'), 'preserved');
4772
+ console.log(JSON.stringify({ scenario: 'packed-node26-cli-rebuild', mode: ${JSON.stringify(mode)},
4773
+ firstObservation: observations.find(line => line.includes('ENOENT')), events: fs.readFileSync(${JSON.stringify(join(output, 'events'))}, 'utf8').trim().split('\\n') }));
4774
+ } finally { controller.abort(); process.stderr.write = write; }
4775
+ })().catch(error => { console.error(error); process.exitCode = 1; });`,
4776
+ ], {
4777
+ cwd: consumer.root,
4778
+ encoding: 'utf8',
4779
+ maxBuffer: 1024 * 1024,
4780
+ });
4781
+ console.log(result.trim());
4782
+ }
4783
+ finally {
4784
+ rmSync(root, { recursive: true, force: true });
4785
+ rmSync(output, { recursive: true, force: true });
4786
+ }
4787
+ });
4304
4788
  function filesystemFixtureScript(output, mode, runtime = 'node24') {
4305
4789
  const script = `
4306
4790
  (async () => {