@aviaratech/ai-delivery 0.3.5 → 0.3.6

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.
@@ -2792,9 +2792,12 @@ test('MCP exposes one implementation surface for native lifecycle commands', ()
2792
2792
  'issue_pr_merge',
2793
2793
  'issue_finish',
2794
2794
  'issue_worktree_create',
2795
+ 'issue_worktree_transition_inspect',
2796
+ 'issue_worktree_transition_apply',
2795
2797
  ]);
2796
2798
  const validInputs = {
2797
2799
  issue_create: { title: 'Synthetic tracking parent' },
2800
+ issue_update: { issueNumber: 17, park: true },
2798
2801
  issue_ready_check: { issueNumber: 17 },
2799
2802
  issue_develop: { issueNumber: 17 },
2800
2803
  issue_verify: { issueNumber: 17 },
@@ -2802,7 +2805,17 @@ test('MCP exposes one implementation surface for native lifecycle commands', ()
2802
2805
  issue_pr_review: { issueNumber: 17, prNumber: 23, artifact: '{}' },
2803
2806
  issue_pr_merge: { issueNumber: 17, prNumber: 23, strategy: 'merge' },
2804
2807
  issue_finish: { issueNumber: 17, prNumber: 23, strategy: 'merge' },
2808
+ issue_worktree_transition_inspect: { issueNumber: 17, purpose: 'active-resume' },
2809
+ issue_worktree_transition_apply: {
2810
+ authority: 'worktree:transition',
2811
+ planPath: '/synthetic/plan.json',
2812
+ expectedPlanId: `sha256:${'a'.repeat(64)}`,
2813
+ relinquishmentCommentId: 101,
2814
+ acceptanceCommentId: 102,
2815
+ },
2805
2816
  };
2817
+ const updateTool = AI_DELIVERY_MCP_TOOLS.find((tool) => tool.name === 'issue_update');
2818
+ assert.equal(updateTool.inputSchema.safeParse({ issueNumber: 17, park: false }).success, false);
2806
2819
  for (const [name, input] of Object.entries(validInputs)) {
2807
2820
  const tool = AI_DELIVERY_MCP_TOOLS.find((candidate) => candidate.name === name);
2808
2821
  assert.ok(tool, `${name} has an MCP definition`);
@@ -2870,6 +2883,9 @@ async function syntheticLifecycle(routing) {
2870
2883
  let trackingParent = false;
2871
2884
  let failFirstBlockerLink = true;
2872
2885
  let failNextProjectSync = false;
2886
+ let interruptNextStatusUpdate = false;
2887
+ let conflictNextStatusReadback = false;
2888
+ let projectStatusWrites = 0;
2873
2889
  let checksPassed = true;
2874
2890
  let prDraft = true;
2875
2891
  let prState = 'open';
@@ -2976,6 +2992,12 @@ async function syntheticLifecycle(routing) {
2976
2992
  nodes: projectStatus === null
2977
2993
  ? []
2978
2994
  : [
2995
+ {
2996
+ id: 'UNRELATED-ITEM',
2997
+ isArchived: false,
2998
+ content: { id: 'UNRELATED-ISSUE' },
2999
+ fieldValueByName: { name: 'Active', optionId: 'STATUS-1' },
3000
+ },
2979
3001
  {
2980
3002
  id: 'ITEM-17',
2981
3003
  isArchived: false,
@@ -2999,19 +3021,29 @@ async function syntheticLifecycle(routing) {
2999
3021
  return { addProjectV2ItemById: { item: { id: 'ITEM-17' } } };
3000
3022
  }
3001
3023
  if (query.includes('UpdateProjectDeliveryStatus')) {
3024
+ assert.equal(variables.projectId, 'PROJECT-1');
3025
+ assert.equal(variables.itemId, 'ITEM-17');
3026
+ projectStatusWrites += 1;
3002
3027
  projectStatus = statusName(String(variables.optionId)) ?? null;
3028
+ if (interruptNextStatusUpdate) {
3029
+ interruptNextStatusUpdate = false;
3030
+ throw new Error('synthetic ambiguous status write');
3031
+ }
3003
3032
  return { updateProjectV2ItemFieldValue: { projectV2Item: { id: 'ITEM-17' } } };
3004
3033
  }
3005
- if (query.includes('ProjectDeliveryItemReadback'))
3034
+ if (query.includes('ProjectDeliveryItemReadback')) {
3035
+ const contentId = conflictNextStatusReadback ? 'UNRELATED-ISSUE' : issue.node_id;
3036
+ conflictNextStatusReadback = false;
3006
3037
  return {
3007
3038
  node: {
3008
3039
  id: 'ITEM-17',
3009
3040
  isArchived: false,
3010
3041
  project: { id: 'PROJECT-1', number: 1 },
3011
- content: { id: issue.node_id },
3042
+ content: { id: contentId },
3012
3043
  fieldValueByName: { name: projectStatus, optionId: statuses[projectStatus] },
3013
3044
  },
3014
3045
  };
3046
+ }
3015
3047
  if (query.includes('blockedBy(first:'))
3016
3048
  return {
3017
3049
  repository: {
@@ -3032,6 +3064,10 @@ async function syntheticLifecycle(routing) {
3032
3064
  blockerOpen = true;
3033
3065
  return { addBlockedBy: { issue: { number: issueNumber } } };
3034
3066
  }
3067
+ if (query.includes('removeBlockedBy')) {
3068
+ blockerOpen = false;
3069
+ return { removeBlockedBy: { issue: { number: issueNumber } } };
3070
+ }
3035
3071
  if (query.includes('RepositoryId'))
3036
3072
  return { repository: { id: 'REPO-1' } };
3037
3073
  if (query.includes('UpdateExactRefs')) {
@@ -3328,6 +3364,41 @@ async function syntheticLifecycle(routing) {
3328
3364
  assert.equal(started.title, issue.title);
3329
3365
  const row = started;
3330
3366
  assert.equal(projectStatus, 'Active');
3367
+ const parked = await updateIssue(context, { issueNumber, park: true });
3368
+ assert.equal(parked.projectStatus, 'Todo');
3369
+ assert.equal(existsSync(row.path), true);
3370
+ assert.equal((await updateIssue(context, { issueNumber, title: issue.title })).projectStatus, 'Todo');
3371
+ await developIssue(context, issueNumber);
3372
+ assert.equal(projectStatus, 'Active');
3373
+ const blockedUpdate = await updateIssue(context, { issueNumber, blockedBy: [9] });
3374
+ assert.deepEqual(blockedUpdate.blockedBy, [9]);
3375
+ assert.equal(blockedUpdate.projectStatus, 'Blocked');
3376
+ assert.equal(projectStatus, 'Waiting');
3377
+ const blockedWrites = projectStatusWrites;
3378
+ assert.equal((await updateIssue(context, { issueNumber, blockedBy: [9] })).projectStatus, 'Blocked');
3379
+ assert.equal(projectStatusWrites, blockedWrites);
3380
+ assert.equal((await updateIssue(context, { issueNumber, park: true })).projectStatus, 'Blocked');
3381
+ await assert.rejects(developIssue(context, issueNumber), /not ready/u);
3382
+ interruptNextStatusUpdate = true;
3383
+ await assert.rejects(updateIssue(context, { issueNumber, blockedBy: [] }), /ambiguous status write/u);
3384
+ assert.equal(blockerOpen, false);
3385
+ assert.equal(projectStatus, 'Queued');
3386
+ const interruptedWrites = projectStatusWrites;
3387
+ const unblockedUpdate = await updateIssue(context, { issueNumber, blockedBy: [] });
3388
+ assert.equal(projectStatusWrites, interruptedWrites);
3389
+ assert.deepEqual(unblockedUpdate.blockedBy, []);
3390
+ assert.equal(unblockedUpdate.projectStatus, 'Todo');
3391
+ conflictNextStatusReadback = true;
3392
+ await assert.rejects(updateIssue(context, { issueNumber, blockedBy: [9] }), /exact issue item/u);
3393
+ assert.equal(blockerOpen, true);
3394
+ assert.equal((await updateIssue(context, { issueNumber, blockedBy: [9] })).projectStatus, 'Blocked');
3395
+ assert.equal((await updateIssue(context, { issueNumber, state: 'closed' })).projectStatus, 'Done');
3396
+ assert.equal((await updateIssue(context, { issueNumber, park: true })).projectStatus, 'Done');
3397
+ assert.equal((await resumeCreatedIssue(context, { issueNumber })).projectStatus, 'Done');
3398
+ assert.equal((await updateIssue(context, { issueNumber, state: 'open', blockedBy: [] })).projectStatus, 'Done');
3399
+ assert.equal(existsSync(row.path), true);
3400
+ await developIssue(context, issueNumber);
3401
+ assert.equal(projectStatus, 'Active');
3331
3402
  writeFileSync(join(row.path, 'change.txt'), 'feature\n');
3332
3403
  git(row.path, 'add', 'change.txt');
3333
3404
  git(row.path, 'commit', '-qm', 'synthetic change');
@@ -3550,9 +3621,9 @@ test.each([
3550
3621
  { remote: 'upstream', divergentOrigin: false },
3551
3622
  { remote: 'upstream', divergentOrigin: true },
3552
3623
  ])('synthetic lifecycle finishes with remote $remote and divergent origin $divergentOrigin', syntheticLifecycle, 15_000);
3553
- test('an unrelated consumer uses public MCP setup through verified configured-author publication, counted review readback, merge and cleanup', async () => {
3624
+ test('an unrelated consumer uses public MCP setup through verified configured-author publication, counted review readback, merge and cleanup', { timeout: 0 }, async () => {
3554
3625
  await syntheticLifecycle({ remote: 'origin', divergentOrigin: false, producerOnboarding: true });
3555
- }, 15_000);
3626
+ });
3556
3627
  test.each(['escape', 'broken', 'cycle'])('output observation rejects %s aliases without running policy commands', async (kind) => {
3557
3628
  const { root, counter } = await fixture();
3558
3629
  const base = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-output-alias-')));