@aviaratech/ai-delivery 0.3.14 → 0.3.16

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.
@@ -2,7 +2,7 @@ import assert from 'node:assert/strict';
2
2
  import { execFileSync, spawn, spawnSync } from 'node:child_process';
3
3
  import { createHash } from 'node:crypto';
4
4
  import { syncBuiltinESMExports } from 'node:module';
5
- import { cpSync, chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, readdirSync, realpathSync, rmSync, symlinkSync, unlinkSync, utimesSync, writeFileSync, } from 'node:fs';
5
+ import { cpSync, chmodSync, existsSync, linkSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, readdirSync, realpathSync, rmSync, symlinkSync, unlinkSync, utimesSync, writeFileSync, } from 'node:fs';
6
6
  import { tmpdir } from 'node:os';
7
7
  import { createServer } from 'node:net';
8
8
  import { dirname, join } from 'node:path';
@@ -3224,11 +3224,16 @@ test('MCP request rejects unsupported legacy options before lifecycle dispatch',
3224
3224
  }
3225
3225
  });
3226
3226
  async function syntheticLifecycle(routing) {
3227
- const created = await fixture({ ...routing, omitRuntimeAdmission: routing.producerOnboarding === true });
3227
+ const created = await fixture({
3228
+ ...routing,
3229
+ personalAuthor: routing.committedDescendantContinuation === true,
3230
+ omitRuntimeAdmission: routing.producerOnboarding === true,
3231
+ });
3228
3232
  const root = created.root;
3229
3233
  let runtimeEntryPath = created.runtimeEntryPath;
3230
3234
  const originalPath = process.env.PATH;
3231
3235
  let restoreTransport;
3236
+ let restoreCheckpointWrites;
3232
3237
  const remoteName = routing.remote;
3233
3238
  assert.equal(defaultBaseRef(root, remoteName), `refs/remotes/${remoteName}/main`);
3234
3239
  const remote = join(root, '.git', 'remote.git');
@@ -3270,6 +3275,11 @@ async function syntheticLifecycle(routing) {
3270
3275
  let review = null;
3271
3276
  let reviewDecision = 'REVIEW_REQUIRED';
3272
3277
  const calls = [];
3278
+ const historicalActor = routing.committedDescendantContinuation ? 'host-user' : 'synthetic-author[bot]';
3279
+ let nativePrAuthor = historicalActor;
3280
+ let nativeClosingIssue = issueNumber;
3281
+ let nativePrRepository;
3282
+ const continuationComments = [];
3273
3283
  const createPayloads = [];
3274
3284
  const statuses = { Queued: 'STATUS-0', Active: 'STATUS-1', Waiting: 'STATUS-2', Shipped: 'STATUS-3' };
3275
3285
  const statusName = (option) => Object.entries(statuses).find(([, id]) => id === option)?.[0];
@@ -3297,14 +3307,26 @@ async function syntheticLifecycle(routing) {
3297
3307
  state: stale ? 'open' : prState,
3298
3308
  draft: prDraft,
3299
3309
  merged_at: !stale && prState === 'closed' ? mergedAt : null,
3310
+ merged: !stale && prState === 'closed',
3300
3311
  merge_commit_sha: mergeSha || null,
3301
3312
  mergeable: true,
3302
3313
  mergeable_state: 'clean',
3303
- user: { login: 'synthetic-author[bot]' },
3304
- head: { sha: headSha, ref: 'issue/17' },
3305
- base: { sha: baseSha, ref: 'main' },
3314
+ user: { login: nativePrAuthor, id: 37, type: routing.committedDescendantContinuation ? 'User' : 'Bot' },
3315
+ head: { sha: headSha, ref: 'issue/17', ...(nativePrRepository ? { repo: { full_name: nativePrRepository } } : {}) },
3316
+ base: { sha: baseSha, ref: 'main', ...(nativePrRepository ? { repo: { full_name: nativePrRepository } } : {}) },
3306
3317
  });
3307
3318
  const graphql = async (query, variables = {}) => {
3319
+ if (query.includes('WorktreeTransitionLineage'))
3320
+ return {
3321
+ repository: {
3322
+ pullRequest: {
3323
+ closingIssuesReferences: {
3324
+ nodes: [{ number: nativeClosingIssue, repository: { nameWithOwner: nativePrRepository } }],
3325
+ pageInfo,
3326
+ },
3327
+ },
3328
+ },
3329
+ };
3308
3330
  if (query.includes('DeliveryReviewDecision'))
3309
3331
  return { repository: { pullRequest: { headRefOid: headSha, reviewDecision } } };
3310
3332
  if (query.includes('ProjectDeliveryConfiguration'))
@@ -3503,6 +3525,10 @@ async function syntheticLifecycle(routing) {
3503
3525
  throw new Error(`Unexpected REST route: ${route}`);
3504
3526
  },
3505
3527
  issues: {
3528
+ listComments: async () => ({ data: continuationComments }),
3529
+ getComment: async (input) => ({
3530
+ data: continuationComments.find((comment) => comment.id === input.comment_id),
3531
+ }),
3506
3532
  create: async (input) => {
3507
3533
  calls.push('issue:create');
3508
3534
  createPayloads.push(input);
@@ -3530,6 +3556,8 @@ async function syntheticLifecycle(routing) {
3530
3556
  return { data: {} };
3531
3557
  },
3532
3558
  update: async (input) => {
3559
+ if (input.body !== undefined)
3560
+ issue.body = input.body;
3533
3561
  if (input.state !== undefined)
3534
3562
  issue.state = input.state;
3535
3563
  return { data: issue };
@@ -3598,20 +3626,21 @@ async function syntheticLifecycle(routing) {
3598
3626
  getCombinedStatusForRef: async () => ({ data: { statuses: [], state: 'success' } }),
3599
3627
  },
3600
3628
  };
3601
- let authenticatedAuthor = 'synthetic-author[bot]';
3629
+ let authenticatedAuthor = historicalActor;
3630
+ let authenticatedCredential = routing.committedDescendantContinuation ? 'user:37' : 'app:201:installation:301';
3602
3631
  const context = {
3603
3632
  root,
3604
3633
  repo: { owner: 'example', repo: 'widget' },
3605
3634
  config,
3606
3635
  configuration,
3607
3636
  clients: {
3608
- authSource: 'app',
3637
+ authSource: routing.committedDescendantContinuation ? 'personal' : 'app',
3609
3638
  role: 'author',
3610
3639
  graphql,
3611
3640
  rest,
3612
3641
  authenticatedAuthor: async () => ({
3613
3642
  actorLogin: authenticatedAuthor,
3614
- credentialIdentity: 'app:201:installation:301',
3643
+ credentialIdentity: authenticatedCredential,
3615
3644
  }),
3616
3645
  },
3617
3646
  };
@@ -3622,6 +3651,7 @@ async function syntheticLifecycle(routing) {
3622
3651
  ...(input.role === 'reviewer'
3623
3652
  ? {
3624
3653
  role: 'reviewer',
3654
+ authSource: 'app',
3625
3655
  appActorLogin: async () => 'synthetic-reviewer[bot]',
3626
3656
  }
3627
3657
  : {}),
@@ -3634,7 +3664,12 @@ async function syntheticLifecycle(routing) {
3634
3664
  restoreDispatchClient = () => dispatchClient.mockRestore();
3635
3665
  return dispatchClient;
3636
3666
  };
3637
- const execution = { repoRoot: root, runtimeEntryPath, identity: 'synthetic-author' };
3667
+ const execution = {
3668
+ repoRoot: root,
3669
+ runtimeEntryPath,
3670
+ identity: config.roles.author.identity,
3671
+ ...(routing.appAuthorContinuation ? { personalAuth: true } : {}),
3672
+ };
3638
3673
  try {
3639
3674
  if (routing.legacyNativeMetadata) {
3640
3675
  enableDispatch();
@@ -3799,6 +3834,8 @@ async function syntheticLifecycle(routing) {
3799
3834
  await assert.rejects(developIssue(context, issueNumber), /closed/u);
3800
3835
  issue.state = 'open';
3801
3836
  assert.equal((await readyCheck(context, issueNumber)).ready, true);
3837
+ if (routing.committedDescendantContinuation)
3838
+ await prepareIssueWorktree({ identity: 'synthetic-preparer', issueNumber, repoRoot: root });
3802
3839
  const started = await startTrackedIssue(context, { issueNumber });
3803
3840
  assert.equal(started.mode, 'existing-issue');
3804
3841
  assert.equal(started.issueNumber, issueNumber);
@@ -3829,6 +3866,32 @@ async function syntheticLifecycle(routing) {
3829
3866
  assert.equal(projectStatusWrites, interruptedWrites);
3830
3867
  assert.deepEqual(unblockedUpdate.blockedBy, []);
3831
3868
  assert.equal(unblockedUpdate.projectStatus, 'Todo');
3869
+ projectStatus = 'Waiting';
3870
+ parentIssueNumber = 11;
3871
+ const heldStatusWrites = projectStatusWrites;
3872
+ const originalBody = issue.body;
3873
+ const metadataBody = `${originalBody}\n\nUpdated tracking context.`;
3874
+ const heldMetadata = await updateIssue(context, { issueNumber, body: metadataBody });
3875
+ assert.equal(heldMetadata.body, metadataBody);
3876
+ assert.equal(heldMetadata.projectStatus, 'Blocked', 'body-only metadata must preserve a held Project status');
3877
+ assert.equal(heldMetadata.state, 'open');
3878
+ assert.deepEqual(heldMetadata.blockedBy, []);
3879
+ assert.equal(heldMetadata.parentIssueNumber, 11);
3880
+ assert.equal(projectStatusWrites, heldStatusWrites);
3881
+ assert.equal((await updateIssue(context, { issueNumber, title: issue.title })).projectStatus, 'Blocked');
3882
+ assert.equal(projectStatusWrites, heldStatusWrites);
3883
+ await updateIssue(context, { issueNumber, body: originalBody });
3884
+ parentIssueNumber = null;
3885
+ for (const input of [{ park: true }, { blockedBy: [] }, { state: 'open' }]) {
3886
+ projectStatus = 'Waiting';
3887
+ const explicitUpdate = await updateIssue(context, { issueNumber, ...input });
3888
+ assert.equal(explicitUpdate.projectStatus, 'Todo');
3889
+ assert.deepEqual(explicitUpdate.blockedBy, []);
3890
+ }
3891
+ projectStatus = 'Waiting';
3892
+ assert.equal((await updateIssue(context, { issueNumber, state: 'closed' })).projectStatus, 'Done');
3893
+ issue.state = 'open';
3894
+ projectStatus = 'Queued';
3832
3895
  conflictNextStatusReadback = true;
3833
3896
  await assert.rejects(updateIssue(context, { issueNumber, blockedBy: [9] }), /exact issue item/u);
3834
3897
  assert.equal(blockerOpen, true);
@@ -3907,14 +3970,14 @@ exec "${realGit}" "$@"
3907
3970
  });
3908
3971
  }
3909
3972
  assert.equal((await listPrs(context)).pullRequests[0]?.number, prNumber);
3910
- assert.equal((await prInfo(context, { prNumber })).authorLogin, 'synthetic-author[bot]');
3973
+ assert.equal((await prInfo(context, { prNumber })).authorLogin, historicalActor);
3911
3974
  assert.equal((await prChecks(context, prNumber)).combinedStatus, 'success');
3912
3975
  assert.equal((await prChecks(context, prNumber)).reviewState.status, 'still-required');
3913
3976
  await assert.rejects(checkoutPr(context, prNumber), /open in-repository branch/u);
3914
3977
  assert.equal(git(remote, 'rev-parse', 'refs/heads/issue/17'), headSha);
3915
3978
  await assert.rejects(mergePr(context, { issueNumber, prNumber }), /submitted independent review/);
3916
3979
  const artifactContent = {
3917
- authorIdentity: 'synthetic-author',
3980
+ authorIdentity: config.roles.author.identity,
3918
3981
  checks: ['verified exact diff'],
3919
3982
  diffScopeHash: digestValue(run.classification.changedPaths),
3920
3983
  elapsedMs: 100,
@@ -3935,7 +3998,12 @@ exec "${realGit}" "$@"
3935
3998
  const artifact = JSON.stringify({ ...artifactContent, artifactId: digestValue(artifactContent) });
3936
3999
  const reviewer = {
3937
4000
  ...context,
3938
- clients: { ...context.clients, role: 'reviewer', appActorLogin: async () => 'synthetic-reviewer' },
4001
+ clients: {
4002
+ ...context.clients,
4003
+ authSource: 'app',
4004
+ role: 'reviewer',
4005
+ appActorLogin: async () => 'synthetic-reviewer',
4006
+ },
3939
4007
  };
3940
4008
  const personalReviewer = {
3941
4009
  ...reviewer,
@@ -3961,11 +4029,11 @@ exec "${realGit}" "$@"
3961
4029
  const attemptPath = join(gitCommonDir(root), 'ai-delivery', 'merge-attempts', String(issueNumber), `${headSha}.json`);
3962
4030
  const attempt = JSON.parse(readFileSync(attemptPath, 'utf8'));
3963
4031
  assert.equal(attempt.operation, 'updateRefs');
3964
- assert.equal(attempt.actor, 'synthetic-author');
3965
- assert.equal(attempt.actorLogin, 'synthetic-author[bot]');
4032
+ assert.equal(attempt.actor, config.roles.author.identity);
4033
+ assert.equal(attempt.actorLogin, historicalActor);
3966
4034
  authenticatedAuthor = 'different-author[bot]';
3967
4035
  await assert.rejects(finishIssue(context, { issueNumber, prNumber, strategy: 'merge' }), /Merge attempt disagrees with its exact source, operation or author/u);
3968
- authenticatedAuthor = 'synthetic-author[bot]';
4036
+ authenticatedAuthor = historicalActor;
3969
4037
  assert.equal(calls.filter((call) => call === 'git:createCommit').length, 1);
3970
4038
  await assert.rejects(finishIssue(context, { issueNumber, prNumber, strategy: 'merge' }), /readback is unresolved/u);
3971
4039
  assert.equal(calls.filter((call) => call === 'git:createCommit').length, 1);
@@ -3984,7 +4052,7 @@ exec "${realGit}" "$@"
3984
4052
  await assert.rejects(finishIssue(context, { issueNumber, prNumber, strategy: 'merge' }), /exact prepared merge intent/u);
3985
4053
  headSha = reviewedHead;
3986
4054
  assert.equal(calls.filter((call) => call === 'git:createCommit').length, 1);
3987
- if (routing.mergedContinuation) {
4055
+ if (routing.mergedContinuation || routing.committedDescendantContinuation) {
3988
4056
  enableDispatch();
3989
4057
  const terminal = await executeTool('issue_pr_merge', { issueNumber, prNumber, strategy: 'merge' }, execution);
3990
4058
  assert.equal(terminal.mergeSha, mergeSha);
@@ -4018,6 +4086,593 @@ exec "${realGit}" "$@"
4018
4086
  saveReceipts(join(gitCommonDir(root), 'ai-delivery', name));
4019
4087
  const ownerPath = join(gitCommonDir(root), 'ai-delivery/worktree-owners');
4020
4088
  const ownerHash = directoryHash(ownerPath);
4089
+ if (routing.committedDescendantContinuation) {
4090
+ const ownerFiles = readdirSync(ownerPath).map((name) => ({
4091
+ path: join(ownerPath, name),
4092
+ bytes: readFileSync(join(ownerPath, name)),
4093
+ }));
4094
+ writeFileSync(join(row.path, 'change.txt'), 'already committed continuation\n');
4095
+ git(row.path, 'add', 'change.txt');
4096
+ git(row.path, 'commit', '-qm', 'commit the unfinished continuation');
4097
+ const descendant = git(row.path, 'rev-parse', 'HEAD');
4098
+ const descendantTree = git(row.path, 'rev-parse', 'HEAD^{tree}');
4099
+ nativePrRepository = 'example/widget';
4100
+ const refuse = async (pattern) => {
4101
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), pattern);
4102
+ const currentRegistry = JSON.parse(readFileSync(registryPath, 'utf8'));
4103
+ assert.deepEqual(currentRegistry.worktrees.filter((entry) => entry.issueNumber === issueNumber), [previous]);
4104
+ assert.equal(git(row.path, 'rev-parse', 'HEAD'), descendant);
4105
+ };
4106
+ if (routing.appAuthorContinuation) {
4107
+ const policyPath = join(root, 'policy.mjs');
4108
+ writeFileSync(policyPath, readFileSync(policyPath, 'utf8').replace(/export const deliverySettings = .*;/u, `export const deliverySettings = ${JSON.stringify({
4109
+ commandPolicy: config.commandPolicy,
4110
+ roles: {
4111
+ ...config.roles,
4112
+ author: {
4113
+ authSource: 'app',
4114
+ credentialEnv: {
4115
+ appId: 'AUTHOR_APP_ID',
4116
+ installationId: 'AUTHOR_INSTALLATION_ID',
4117
+ privateKeyPath: 'AUTHOR_KEY_PATH',
4118
+ },
4119
+ identity: config.roles.author.identity,
4120
+ },
4121
+ },
4122
+ })};`));
4123
+ const admissionPath = join(gitCommonDir(root), 'ai-delivery/runtime-admission.json');
4124
+ const { admissionId: _id, ...admission } = JSON.parse(readFileSync(admissionPath, 'utf8'));
4125
+ admission.configDigest = (await loadDeliveryConfig(root, { personalAuth: true })).configDigest;
4126
+ execution.identity = 'personal';
4127
+ writeFileSync(admissionPath, JSON.stringify({ ...admission, admissionId: digestValue(admission) }));
4128
+ await refuse(/requires configured personal author/u);
4129
+ for (const savedReceipt of [...oldReceipts, ...ownerFiles])
4130
+ assert.deepEqual(readFileSync(savedReceipt.path), savedReceipt.bytes);
4131
+ return;
4132
+ }
4133
+ authenticatedAuthor = 'different-user';
4134
+ await refuse(/authenticated host author/u);
4135
+ authenticatedAuthor = historicalActor;
4136
+ authenticatedCredential = 'user:99';
4137
+ await refuse(/authenticated host author/u);
4138
+ authenticatedCredential = 'unattested';
4139
+ await refuse(/user identity.*unavailable/u);
4140
+ authenticatedCredential = 'user:37';
4141
+ nativePrAuthor = 'different-user';
4142
+ await refuse(/authenticated host author/u);
4143
+ nativePrAuthor = historicalActor;
4144
+ nativeClosingIssue = 18;
4145
+ await refuse(/Native PR lineage/u);
4146
+ nativeClosingIssue = issueNumber;
4147
+ nativePrRepository = 'other/widget';
4148
+ await refuse(/Native PR lineage/u);
4149
+ nativePrRepository = 'example/widget';
4150
+ for (const directory of custody) {
4151
+ const file = oldReceipts.find((file) => file.path.includes(`/${directory}/`) && file.path.endsWith(`/${headSha}.json`));
4152
+ assert.ok(file, `historical ${directory} fixture`);
4153
+ unlinkSync(file.path);
4154
+ try {
4155
+ await refuse(/(?:ENOENT|missing|lacks|manifest|publication|terminal|review|intent|custody)/u);
4156
+ }
4157
+ finally {
4158
+ writeFileSync(file.path, file.bytes, { mode: 0o600 });
4159
+ }
4160
+ const forged = JSON.parse(file.bytes.toString());
4161
+ const seal = Object.keys(forged).find((key) => /^(?:manifest|publication|receipt|intent|attempt|result|merge)Id$/u.test(key));
4162
+ assert.ok(seal);
4163
+ forged[seal] = `sha256:${'f'.repeat(64)}`;
4164
+ writeFileSync(file.path, JSON.stringify(forged));
4165
+ try {
4166
+ await refuse(/(?:identity|invalid|disagrees|not bound|corrupt)/iu);
4167
+ }
4168
+ finally {
4169
+ writeFileSync(file.path, file.bytes);
4170
+ }
4171
+ }
4172
+ const witness = ownerFiles.find((file) => JSON.parse(file.bytes.toString()).identity === 'synthetic-preparer');
4173
+ assert.ok(witness);
4174
+ unlinkSync(witness.path);
4175
+ try {
4176
+ await refuse(/ownership witness/u);
4177
+ }
4178
+ finally {
4179
+ writeFileSync(witness.path, witness.bytes, { mode: 0o600 });
4180
+ }
4181
+ writeFileSync(join(row.path, 'change.txt'), 'dirty continuation\n');
4182
+ await refuse(/clean|uncommitted|dirty/iu);
4183
+ git(row.path, 'restore', 'change.txt');
4184
+ const unrelatedHead = git(row.path, 'commit-tree', descendantTree, '-m', 'unrelated root');
4185
+ git(row.path, 'reset', '--hard', unrelatedHead);
4186
+ try {
4187
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /strict committed descendant/u);
4188
+ }
4189
+ finally {
4190
+ git(row.path, 'reset', '--hard', descendant);
4191
+ }
4192
+ const registryData = JSON.parse(registryBytes.toString());
4193
+ for (const conflicting of [previous, { ...previous, type: 'standalone', issueNumber: 18 }]) {
4194
+ writeFileSync(registryPath, JSON.stringify({ worktrees: [...registryData.worktrees, conflicting] }));
4195
+ try {
4196
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /duplicate|overlaps/u);
4197
+ }
4198
+ finally {
4199
+ writeFileSync(registryPath, registryBytes);
4200
+ }
4201
+ }
4202
+ const writerPath = join(gitCommonDir(root), 'ai-delivery/writers@1', `${digestValue(row.path).slice(7)}.json`);
4203
+ atomicJson.writePrivateJsonFileAtomically(writerPath, { unknown: 'writer-custody' });
4204
+ const writerBytes = readFileSync(writerPath);
4205
+ try {
4206
+ await refuse(/writer absence/u);
4207
+ assert.deepEqual(readFileSync(writerPath), writerBytes);
4208
+ }
4209
+ finally {
4210
+ unlinkSync(writerPath);
4211
+ }
4212
+ const legacyIntentPath = join(ownerPath, 'issue-17.transition.json');
4213
+ atomicJson.writePrivateJsonFileAtomically(legacyIntentPath, { unknown: 'legacy-transition' });
4214
+ try {
4215
+ await refuse(/transition.*incomplete/u);
4216
+ }
4217
+ finally {
4218
+ unlinkSync(legacyIntentPath);
4219
+ }
4220
+ let crashIndex = 0;
4221
+ const publishedCheckpoints = [];
4222
+ const withDirectorySyncProbe = async (directory, failure, operation) => {
4223
+ const fs = (await import('node:fs')).default;
4224
+ const originalOpen = fs.openSync;
4225
+ const originalSync = fs.fsyncSync;
4226
+ const originalClose = fs.closeSync;
4227
+ const directories = new Set();
4228
+ const probe = { attempts: 0, fail: true };
4229
+ const opened = vi.spyOn(fs, 'openSync').mockImplementation((path, flags, mode) => {
4230
+ const descriptor = originalOpen(path, flags, mode);
4231
+ if (path === directory)
4232
+ directories.add(descriptor);
4233
+ return descriptor;
4234
+ });
4235
+ const synced = vi.spyOn(fs, 'fsyncSync').mockImplementation((descriptor) => {
4236
+ if (directories.has(descriptor)) {
4237
+ probe.attempts += 1;
4238
+ if (probe.fail)
4239
+ throw new Error(failure);
4240
+ }
4241
+ originalSync(descriptor);
4242
+ });
4243
+ const closed = vi.spyOn(fs, 'closeSync').mockImplementation((descriptor) => {
4244
+ directories.delete(descriptor);
4245
+ originalClose(descriptor);
4246
+ });
4247
+ syncBuiltinESMExports();
4248
+ try {
4249
+ await operation(probe);
4250
+ }
4251
+ finally {
4252
+ closed.mockRestore();
4253
+ synced.mockRestore();
4254
+ opened.mockRestore();
4255
+ syncBuiltinESMExports();
4256
+ }
4257
+ };
4258
+ const crashAfterDirectoryCreation = (directory, marker) => {
4259
+ assert.equal(existsSync(directory), false);
4260
+ const directoryChild = spawnSync(process.execPath, [
4261
+ '--input-type=module',
4262
+ '-e',
4263
+ `
4264
+ import fs from 'node:fs';
4265
+ import { syncBuiltinESMExports } from 'node:module';
4266
+ const mkdir = fs.mkdirSync;
4267
+ fs.mkdirSync = (path, options) => {
4268
+ const result = mkdir(path, options);
4269
+ if (path === ${JSON.stringify(directory)}) {
4270
+ const stat = fs.lstatSync(path);
4271
+ fs.writeFileSync(${JSON.stringify(marker)}, JSON.stringify({
4272
+ path, pid: process.pid, dev: stat.dev, ino: stat.ino, uid: stat.uid, mode: stat.mode,
4273
+ }), { mode: 0o600 });
4274
+ process.kill(process.pid, 'SIGKILL');
4275
+ }
4276
+ return result;
4277
+ };
4278
+ syncBuiltinESMExports();
4279
+ const { ensurePrivateDirectoryDurably } = await import(${JSON.stringify(new URL('./utils/atomicJson.js', import.meta.url).href)});
4280
+ ensurePrivateDirectoryDurably(${JSON.stringify(directory)});
4281
+ `,
4282
+ ], { encoding: 'utf8', maxBuffer: 1024 * 1024 });
4283
+ assert.equal(directoryChild.signal, 'SIGKILL', directoryChild.stderr);
4284
+ const directoryProof = JSON.parse(readFileSync(marker, 'utf8'));
4285
+ assert.equal(directoryProof.path, directory);
4286
+ assert.equal(directoryProof.pid, directoryChild.pid);
4287
+ assert.throws(() => process.kill(directoryProof.pid, 0), /ESRCH/u);
4288
+ const directoryStat = lstatSync(directory);
4289
+ assert.ok(directoryStat.isDirectory() && !directoryStat.isSymbolicLink());
4290
+ assert.equal(directoryStat.dev, directoryProof.dev);
4291
+ assert.equal(directoryStat.ino, directoryProof.ino);
4292
+ assert.equal(directoryStat.uid, directoryProof.uid);
4293
+ assert.equal(directoryStat.mode & 0o077, 0);
4294
+ console.info('continuation directory crash evidence', JSON.stringify(directoryProof));
4295
+ };
4296
+ if (routing.crashCheckpoints) {
4297
+ const continuationRoot = join(gitCommonDir(root), 'ai-delivery/continuations');
4298
+ assert.equal(existsSync(continuationRoot), false);
4299
+ crashAfterDirectoryCreation(continuationRoot, join(gitCommonDir(root), 'continuation-root-crash.json'));
4300
+ const kinds = ['plan', 'intent', 'witness', 'completion'];
4301
+ const originalWrite = atomicJson.writePrivateJsonFileAtomically;
4302
+ const crashing = vi.spyOn(atomicJson, 'writePrivateJsonFileAtomically').mockImplementation((path, value) => {
4303
+ const kind = path.includes('/worktree-owners/') ? 'witness' : path.split('.').at(-2);
4304
+ if (kind !== kinds[Math.floor(crashIndex / 2)])
4305
+ return originalWrite(path, value);
4306
+ const after = crashIndex % 2 === 1;
4307
+ const marker = join(gitCommonDir(root), `continuation-crash-${String(crashIndex)}.json`);
4308
+ const child = spawnSync(process.execPath, [
4309
+ '--input-type=module',
4310
+ '-e',
4311
+ `
4312
+ import fs from 'node:fs';
4313
+ import { createHash } from 'node:crypto';
4314
+ import { syncBuiltinESMExports } from 'node:module';
4315
+ const path = ${JSON.stringify(path)};
4316
+ for (const method of ['renameSync', 'linkSync']) {
4317
+ const publish = fs[method];
4318
+ fs[method] = (temporary, destination) => {
4319
+ if (destination !== path) return publish(temporary, destination);
4320
+ const stat = fs.lstatSync(temporary);
4321
+ const digest = createHash('sha256').update(fs.readFileSync(temporary)).digest('hex');
4322
+ if (${String(after)}) publish(temporary, destination);
4323
+ fs.writeFileSync(${JSON.stringify(marker)}, JSON.stringify({
4324
+ path, temporary, pid: process.pid, dev: stat.dev, ino: stat.ino,
4325
+ uid: stat.uid, mode: stat.mode, digest,
4326
+ }), { mode: 0o600 });
4327
+ process.kill(process.pid, 'SIGKILL');
4328
+ throw new Error('SIGKILL did not terminate checkpoint publisher');
4329
+ };
4330
+ }
4331
+ syncBuiltinESMExports();
4332
+ const { writeCommittedContinuationCheckpoint } = await import(${JSON.stringify(new URL('./services/worktreeRegistry.js', import.meta.url).href)});
4333
+ writeCommittedContinuationCheckpoint(path, ${JSON.stringify(value)});
4334
+ `,
4335
+ ], { encoding: 'utf8', maxBuffer: 1024 * 1024 });
4336
+ assert.equal(child.signal, 'SIGKILL', child.stderr);
4337
+ const proof = JSON.parse(readFileSync(marker, 'utf8'));
4338
+ assert.equal(proof.pid, child.pid);
4339
+ assert.equal(proof.path, path);
4340
+ assert.throws(() => process.kill(proof.pid, 0), /ESRCH/u);
4341
+ if (after) {
4342
+ const stat = lstatSync(path);
4343
+ assert.ok(stat.isFile() && !stat.isSymbolicLink());
4344
+ assert.equal(stat.nlink, 1, `${kind} publication must retain a single link after process death`);
4345
+ assert.equal(stat.mode & 0o077, 0);
4346
+ assert.equal(stat.dev, proof.dev);
4347
+ assert.equal(stat.ino, proof.ino);
4348
+ assert.equal(stableJson(JSON.parse(readFileSync(path, 'utf8'))), stableJson(value));
4349
+ assert.equal(existsSync(proof.temporary), false);
4350
+ publishedCheckpoints.push({ path, bytes: readFileSync(path) });
4351
+ }
4352
+ else {
4353
+ assert.equal(existsSync(path), false);
4354
+ const stat = lstatSync(proof.temporary);
4355
+ assert.ok(stat.isFile() && !stat.isSymbolicLink());
4356
+ assert.equal(stat.nlink, 1);
4357
+ assert.equal(stat.uid, proof.uid);
4358
+ assert.equal(stat.mode, proof.mode);
4359
+ assert.equal(stat.dev, proof.dev);
4360
+ assert.equal(stat.ino, proof.ino);
4361
+ assert.equal(createHash('sha256').update(readFileSync(proof.temporary)).digest('hex'), proof.digest);
4362
+ unlinkSync(proof.temporary);
4363
+ }
4364
+ console.info('continuation checkpoint crash recovered', JSON.stringify({
4365
+ kind,
4366
+ after,
4367
+ signal: child.signal,
4368
+ proof,
4369
+ temporaryRemoved: !existsSync(proof.temporary),
4370
+ targetExists: existsSync(path),
4371
+ }));
4372
+ crashIndex += 1;
4373
+ throw new Error('synthetic SIGKILL checkpoint publication');
4374
+ });
4375
+ restoreCheckpointWrites = () => crashing.mockRestore();
4376
+ for (const directory of [dirname(continuationRoot), gitCommonDir(root)])
4377
+ await withDirectorySyncProbe(directory, 'synthetic continuation namespace parent sync failure', async (probe) => {
4378
+ await refuse(/synthetic continuation namespace parent sync failure/u);
4379
+ assert.ok(probe.attempts > 0);
4380
+ assert.deepEqual(readdirSync(continuationRoot), [String(issueNumber)]);
4381
+ assert.equal(readdirSync(join(continuationRoot, String(issueNumber))).length, 0);
4382
+ probe.fail = false;
4383
+ const beforeRetry = probe.attempts;
4384
+ await refuse(/synthetic SIGKILL checkpoint publication/u);
4385
+ assert.ok(probe.attempts > beforeRetry, 'supported replay must sync the existing namespace parent');
4386
+ });
4387
+ assert.equal(crashIndex, 2);
4388
+ }
4389
+ const first = await executeTool('issue_develop', { issueNumber }, execution).catch((error) => error);
4390
+ assert.ok(first instanceof Error);
4391
+ assert.match(first.message, /exact native.*acceptance/u);
4392
+ const planPath = first.message.split('Saved plan: ')[1];
4393
+ const saved = JSON.parse(readFileSync(planPath, 'utf8'));
4394
+ if (routing.crashCheckpoints) {
4395
+ const foreignLink = join(gitCommonDir(root), 'foreign-continuation-plan-link.json');
4396
+ linkSync(planPath, foreignLink);
4397
+ try {
4398
+ await refuse(/private regular file/u);
4399
+ assert.equal(lstatSync(planPath).ino, lstatSync(foreignLink).ino);
4400
+ }
4401
+ finally {
4402
+ unlinkSync(foreignLink);
4403
+ }
4404
+ }
4405
+ assert.deepEqual(readFileSync(registryPath), registryBytes);
4406
+ const unrelated = {
4407
+ branch: 'unrelated/retained',
4408
+ createdAt: previous.createdAt,
4409
+ identity: 'separate-worker',
4410
+ path: join(root, '.worktrees/unrelated-retained'),
4411
+ status: 'active',
4412
+ type: 'standalone',
4413
+ updatedAt: previous.updatedAt,
4414
+ };
4415
+ writeFileSync(registryPath, JSON.stringify({ worktrees: [...registryData.worktrees, unrelated] }));
4416
+ const subject = 'https://api.github.com/repos/example/widget/issues/17';
4417
+ continuationComments.push({
4418
+ id: 101,
4419
+ issue_url: subject,
4420
+ user: { login: 'host-user', id: 37, type: 'User' },
4421
+ body: saved.operatorBody,
4422
+ });
4423
+ const acceptance = { ...JSON.parse(saved.reviewerBody), authorityCommentId: 101 };
4424
+ continuationComments.push({
4425
+ id: 102,
4426
+ issue_url: subject,
4427
+ user: { login: 'synthetic-reviewer[bot]', id: 38, type: 'Bot' },
4428
+ body: stableJson(acceptance),
4429
+ });
4430
+ for (const [index, patch] of [
4431
+ [0, { issue_url: 'https://api.github.com/repos/example/widget/issues/18' }],
4432
+ [0, { user: { login: 'host-user', id: 99, type: 'User' } }],
4433
+ [0, { body: '{}' }],
4434
+ [1, { user: { login: 'different-reviewer[bot]', id: 38, type: 'Bot' } }],
4435
+ [1, { user: { login: 'synthetic-reviewer[bot]', id: 38, type: 'User' } }],
4436
+ [1, { body: '{}' }],
4437
+ ]) {
4438
+ const original = continuationComments[index];
4439
+ continuationComments[index] = { ...original, ...patch };
4440
+ try {
4441
+ await refuse(/(?:exact native.*acceptance|Native.*(?:missing|changed))/u);
4442
+ }
4443
+ finally {
4444
+ continuationComments[index] = original;
4445
+ }
4446
+ }
4447
+ continuationComments.push({
4448
+ id: 103,
4449
+ issue_url: subject,
4450
+ user: { login: 'host-user', id: 37, type: 'User' },
4451
+ body: stableJson({
4452
+ schemaVersion: 'ai-delivery.worktree-continuation-revocation@1',
4453
+ planId: saved.plan.planId,
4454
+ authorityCommentId: 101,
4455
+ }),
4456
+ });
4457
+ try {
4458
+ await refuse(/revoked/u);
4459
+ assert.equal(existsSync(planPath.replace(/\.plan\.json$/u, '.intent.json')), false);
4460
+ }
4461
+ finally {
4462
+ continuationComments.pop();
4463
+ }
4464
+ const refuseDifferentRuntime = async () => {
4465
+ const admissionPath = join(gitCommonDir(root), 'ai-delivery/runtime-admission.json');
4466
+ const originalAdmission = readFileSync(admissionPath);
4467
+ const { admissionId: _id, ...content } = JSON.parse(originalAdmission.toString());
4468
+ const changed = { ...content, sourceArchiveSha256: digestValue('another valid admitted archive') };
4469
+ const before = readFileSync(registryPath);
4470
+ writeFileSync(admissionPath, JSON.stringify({ ...changed, admissionId: digestValue(changed) }));
4471
+ try {
4472
+ const admitted = await assertDeliveryRuntimeAdmitted({ repoRoot: root, runtimeEntryPath });
4473
+ assert.equal(admitted.sourceArchiveSha256, changed.sourceArchiveSha256);
4474
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /runtime admission.*(?:changed|drifted)|plan drifted/u);
4475
+ assert.deepEqual(readFileSync(registryPath), before);
4476
+ for (const savedReceipt of [...oldReceipts, ...ownerFiles])
4477
+ assert.deepEqual(readFileSync(savedReceipt.path), savedReceipt.bytes);
4478
+ }
4479
+ finally {
4480
+ writeFileSync(admissionPath, originalAdmission);
4481
+ }
4482
+ };
4483
+ await refuseDifferentRuntime();
4484
+ if (routing.crashCheckpoints) {
4485
+ const evidenceDirectory = join(gitCommonDir(root), 'ai-delivery/continuations/evidence');
4486
+ crashAfterDirectoryCreation(evidenceDirectory, join(gitCommonDir(root), 'continuation-directory-crash.json'));
4487
+ const retainedDirectory = join(gitCommonDir(root), 'retained-continuation-evidence');
4488
+ const fs = (await import('node:fs')).default;
4489
+ fs.renameSync(evidenceDirectory, retainedDirectory);
4490
+ symlinkSync(retainedDirectory, evidenceDirectory, 'dir');
4491
+ try {
4492
+ await refuse(/evidence requires a private directory/u);
4493
+ assert.equal(readdirSync(retainedDirectory).length, 0);
4494
+ }
4495
+ finally {
4496
+ unlinkSync(evidenceDirectory);
4497
+ fs.renameSync(retainedDirectory, evidenceDirectory);
4498
+ }
4499
+ await withDirectorySyncProbe(dirname(evidenceDirectory), 'synthetic evidence directory sync failure', async (probe) => {
4500
+ await refuse(/synthetic evidence directory sync failure/u);
4501
+ assert.ok(probe.attempts > 0);
4502
+ assert.equal(existsSync(planPath.replace(/\.plan\.json$/u, '.intent.json')), false);
4503
+ assert.equal(readdirSync(evidenceDirectory).length, 0);
4504
+ probe.fail = false;
4505
+ const beforeRetry = probe.attempts;
4506
+ for (let attempt = 0; attempt < 6; attempt += 1) {
4507
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /synthetic SIGKILL checkpoint publication/u);
4508
+ assert.equal(git(row.path, 'rev-parse', 'HEAD'), descendant);
4509
+ assert.equal(git(row.path, 'rev-parse', 'HEAD^{tree}'), descendantTree);
4510
+ for (const savedReceipt of [...oldReceipts, ...ownerFiles])
4511
+ assert.deepEqual(readFileSync(savedReceipt.path), savedReceipt.bytes);
4512
+ if (attempt < 5 && existsSync(planPath.replace(/\.plan\.json$/u, '.intent.json')))
4513
+ await assert.rejects(executeTool('issue_verify', { issueNumber }, execution), /continuation.*(?:pending|incomplete)/u);
4514
+ }
4515
+ assert.ok(probe.attempts > beforeRetry, 'retry must durably publish the existing evidence directory');
4516
+ });
4517
+ assert.equal(crashIndex, 8);
4518
+ for (const checkpoint of publishedCheckpoints)
4519
+ assert.deepEqual(readFileSync(checkpoint.path), checkpoint.bytes);
4520
+ restoreCheckpointWrites?.();
4521
+ restoreCheckpointWrites = undefined;
4522
+ const before = readFileSync(registryPath);
4523
+ fs.renameSync(evidenceDirectory, retainedDirectory);
4524
+ symlinkSync(retainedDirectory, evidenceDirectory, 'dir');
4525
+ try {
4526
+ for (const tool of ['issue_develop', 'issue_verify'])
4527
+ await assert.rejects(executeTool(tool, { issueNumber }, execution), /evidence requires a private directory/u);
4528
+ assert.deepEqual(readFileSync(registryPath), before);
4529
+ for (const checkpoint of publishedCheckpoints)
4530
+ assert.deepEqual(readFileSync(checkpoint.path), checkpoint.bytes);
4531
+ }
4532
+ finally {
4533
+ unlinkSync(evidenceDirectory);
4534
+ fs.renameSync(retainedDirectory, evidenceDirectory);
4535
+ }
4536
+ for (const directory of [
4537
+ gitCommonDir(root),
4538
+ join(gitCommonDir(root), 'ai-delivery'),
4539
+ dirname(dirname(planPath)),
4540
+ dirname(planPath),
4541
+ ])
4542
+ await withDirectorySyncProbe(directory, 'synthetic completion directory sync failure', async (probe) => {
4543
+ for (const tool of ['issue_develop', 'issue_verify'])
4544
+ await assert.rejects(executeTool(tool, { issueNumber }, execution), /synthetic completion directory sync failure/u);
4545
+ assert.ok(probe.attempts > 0);
4546
+ assert.deepEqual(readFileSync(registryPath), before);
4547
+ for (const checkpoint of publishedCheckpoints)
4548
+ assert.deepEqual(readFileSync(checkpoint.path), checkpoint.bytes);
4549
+ for (const savedReceipt of [...oldReceipts, ...ownerFiles])
4550
+ assert.deepEqual(readFileSync(savedReceipt.path), savedReceipt.bytes);
4551
+ probe.fail = false;
4552
+ const beforeRetry = probe.attempts;
4553
+ await executeTool('issue_develop', { issueNumber }, execution);
4554
+ assert.ok(probe.attempts > beforeRetry, 'supported replay must sync the existing completion directory');
4555
+ });
4556
+ }
4557
+ if (routing.interruptContinuation !== false) {
4558
+ const originalWrite = atomicJson.writeJsonFileAtomically;
4559
+ for (const afterWrite of [false, true]) {
4560
+ const interrupted = vi.spyOn(atomicJson, 'writeJsonFileAtomically').mockImplementation((...args) => {
4561
+ if (args[0] === registryPath) {
4562
+ if (afterWrite)
4563
+ originalWrite(...args);
4564
+ throw new Error('synthetic continuation registry interruption');
4565
+ }
4566
+ return originalWrite(...args);
4567
+ });
4568
+ try {
4569
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /synthetic continuation registry interruption/u);
4570
+ }
4571
+ finally {
4572
+ interrupted.mockRestore();
4573
+ }
4574
+ await assert.rejects(executeTool('issue_verify', { issueNumber }, execution), /continuation.*(?:pending|incomplete)/u);
4575
+ await assert.rejects(executeTool('issue_update', { issueNumber, body: issue.body }, execution), /continuation.*(?:pending|incomplete)/u);
4576
+ await assert.rejects(executeTool('issue_start', { issueNumber, resumeCreated: true, develop: true }, execution), /continuation.*(?:pending|incomplete)/u);
4577
+ assert.equal(git(row.path, 'rev-parse', 'HEAD'), descendant);
4578
+ assert.equal(git(row.path, 'rev-parse', 'HEAD^{tree}'), descendantTree);
4579
+ for (const savedReceipt of [...oldReceipts, ...ownerFiles])
4580
+ assert.deepEqual(readFileSync(savedReceipt.path), savedReceipt.bytes);
4581
+ }
4582
+ const intentPath = planPath.replace(/\.plan\.json$/u, '.intent.json');
4583
+ await refuseDifferentRuntime();
4584
+ const intentBytes = readFileSync(intentPath);
4585
+ writeFileSync(intentPath, '{}');
4586
+ try {
4587
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /invalid|expected/iu);
4588
+ }
4589
+ finally {
4590
+ writeFileSync(intentPath, intentBytes);
4591
+ }
4592
+ const beforeimageDirectory = join(gitCommonDir(root), 'ai-delivery/continuations/evidence');
4593
+ const beforeimage = join(beforeimageDirectory, readdirSync(beforeimageDirectory)[0]);
4594
+ const beforeimageBytes = readFileSync(beforeimage);
4595
+ writeFileSync(beforeimage, 'corrupt original');
4596
+ try {
4597
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /(?:evidence|beforeimage).*changed/u);
4598
+ }
4599
+ finally {
4600
+ writeFileSync(beforeimage, beforeimageBytes);
4601
+ }
4602
+ writeFileSync(join(row.path, 'change.txt'), 'incompatible replay source\n');
4603
+ git(row.path, 'add', 'change.txt');
4604
+ git(row.path, 'commit', '-qm', 'incompatible replay');
4605
+ try {
4606
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /plan drifted/u);
4607
+ }
4608
+ finally {
4609
+ git(row.path, 'reset', '--hard', descendant);
4610
+ }
4611
+ const authorityUser = continuationComments[0].user;
4612
+ continuationComments[0].user = { login: 'host-user', id: 99, type: 'User' };
4613
+ try {
4614
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /operator.*(?:missing|changed)/u);
4615
+ }
4616
+ finally {
4617
+ continuationComments[0].user = authorityUser;
4618
+ }
4619
+ const reviewBody = continuationComments[1].body;
4620
+ continuationComments[1].body = '{}';
4621
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /acceptance.*(?:missing|changed)/u);
4622
+ continuationComments[1].body = reviewBody;
4623
+ continuationComments.push({
4624
+ id: 103,
4625
+ issue_url: subject,
4626
+ user: { login: 'host-user', id: 37, type: 'User' },
4627
+ body: stableJson({
4628
+ schemaVersion: 'ai-delivery.worktree-continuation-revocation@1',
4629
+ planId: saved.plan.planId,
4630
+ authorityCommentId: 101,
4631
+ }),
4632
+ });
4633
+ await assert.rejects(executeTool('issue_develop', { issueNumber }, execution), /revoked/u);
4634
+ continuationComments.pop();
4635
+ }
4636
+ projectStatus = null;
4637
+ failNextProjectSync = true;
4638
+ await assert.rejects(executeTool('issue_start', { issueNumber }, execution), /synthetic Project interruption/u);
4639
+ const resumed = await executeTool('issue_develop', { issueNumber }, execution);
4640
+ assert.deepEqual(resumed, { path: previous.path, branch: previous.branch });
4641
+ const current = getIssueWorktreeStrict(issueNumber, root);
4642
+ assert.equal(current.identity, 'host-author');
4643
+ assert.equal(current.status, 'active');
4644
+ assert.equal(current.prNumber, undefined);
4645
+ assert.equal(current.createdAt, previous.createdAt);
4646
+ assert.equal(git(row.path, 'rev-parse', 'HEAD'), descendant);
4647
+ assert.equal(git(row.path, 'status', '--porcelain'), '');
4648
+ assert.equal(existsSync(join(gitCommonDir(root), 'ai-delivery/merges', '17', `${descendant}.json`)), false);
4649
+ const continued = await verifyIssue({ issueNumber, repoRoot: row.path });
4650
+ assert.equal(continued.classification.head.sha, descendant);
4651
+ writeFileSync(join(row.path, 'change.txt'), 'later ordinary commit\n');
4652
+ git(row.path, 'add', 'change.txt');
4653
+ git(row.path, 'commit', '-qm', 'ordinary work after completed continuation');
4654
+ const futureHead = git(row.path, 'rev-parse', 'HEAD');
4655
+ await executeTool('issue_verify', { issueNumber }, execution);
4656
+ assert.equal(loadVerifiedRun(row.path, issueNumber).classification.head.sha, futureHead);
4657
+ for (const savedReceipt of [...oldReceipts, ...ownerFiles])
4658
+ assert.deepEqual(readFileSync(savedReceipt.path), savedReceipt.bytes);
4659
+ const finalRegistry = JSON.parse(readFileSync(registryPath, 'utf8'));
4660
+ assert.deepEqual(finalRegistry.worktrees.find((entry) => entry.branch === unrelated.branch), unrelated);
4661
+ assert.equal(calls.filter((call) => call === 'git:createCommit').length, 1);
4662
+ if (routing.continuationResult)
4663
+ routing.continuationResult.value = {
4664
+ identity: current.identity,
4665
+ status: current.status,
4666
+ prNumber: current.prNumber,
4667
+ createdAtPreserved: current.createdAt === previous.createdAt,
4668
+ issueRows: finalRegistry.worktrees.filter((entry) => entry.branch === previous.branch).length,
4669
+ unrelatedPreserved: true,
4670
+ originalBytesPreserved: true,
4671
+ descendantVerified: continued.classification.head.sha === descendant,
4672
+ futureVerified: loadVerifiedRun(row.path, issueNumber).classification.head.sha === futureHead,
4673
+ };
4674
+ return;
4675
+ }
4021
4676
  const publicationPath = join(gitCommonDir(root), 'ai-delivery/publications', String(issueNumber), `${headSha}.json`);
4022
4677
  const publicationBytes = readFileSync(publicationPath);
4023
4678
  const { publicationId: _publicationId, ...publicationContent } = JSON.parse(publicationBytes.toString());
@@ -4156,6 +4811,7 @@ exec "${realGit}" "$@"
4156
4811
  assert.equal(calls.filter((call) => call === 'issue:create').length, beforeTracking + 1);
4157
4812
  }
4158
4813
  finally {
4814
+ restoreCheckpointWrites?.();
4159
4815
  restoreTransport?.();
4160
4816
  process.env.PATH = originalPath;
4161
4817
  restoreDispatchClient?.();
@@ -4168,6 +4824,29 @@ test.each([
4168
4824
  { remote: 'upstream', divergentOrigin: true },
4169
4825
  ])('synthetic lifecycle finishes with remote $remote and divergent origin $divergentOrigin', syntheticLifecycle, 15_000);
4170
4826
  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 }));
4827
+ test('supported develop reconciles authenticated host custody for an already committed descendant', { timeout: 0 }, async () => {
4828
+ const input = { remote: 'origin', divergentOrigin: false, committedDescendantContinuation: true };
4829
+ const recovered = {};
4830
+ const uninterrupted = {};
4831
+ await syntheticLifecycle({ ...input, continuationResult: recovered });
4832
+ await syntheticLifecycle({ ...input, interruptContinuation: false, continuationResult: uninterrupted });
4833
+ assert.ok(recovered.value);
4834
+ assert.ok(uninterrupted.value);
4835
+ assert.deepEqual(recovered.value, uninterrupted.value);
4836
+ });
4837
+ test('committed descendant checkpoint publication recovers from SIGKILL before and after every rename', { timeout: 0 }, async () => syntheticLifecycle({
4838
+ remote: 'origin',
4839
+ divergentOrigin: false,
4840
+ committedDescendantContinuation: true,
4841
+ interruptContinuation: false,
4842
+ crashCheckpoints: true,
4843
+ }));
4844
+ test('committed descendant continuation refuses configured App authors using a personal override', { timeout: 0 }, async () => syntheticLifecycle({
4845
+ remote: 'origin',
4846
+ divergentOrigin: false,
4847
+ committedDescendantContinuation: true,
4848
+ appAuthorContinuation: true,
4849
+ }));
4171
4850
  test('supported native metadata updates a legacy target without adopting its source or receipts', { timeout: 0 }, async () => syntheticLifecycle({ remote: 'origin', divergentOrigin: false, legacyNativeMetadata: true }));
4172
4851
  test('an unrelated consumer uses public MCP setup through verified configured-author publication, counted review readback, merge and cleanup', { timeout: 0 }, async () => {
4173
4852
  await syntheticLifecycle({ remote: 'origin', divergentOrigin: false, producerOnboarding: true });