Fix/expand command tag corruption (#827)

* fix(expand): Fix tag corruption in expand command - Fix tag parameter passing through MCP expand-task flow - Add tag parameter to direct function and tool registration - Fix contextGatherer method name from _buildDependencyContext to _buildDependencyGraphs - Add comprehensive test coverage for tag handling in expand-task - Ensures tagged task structure is preserved during expansion - Prevents corruption when tag is undefined. Fixes expand command causing tag corruption in tagged task lists. All existing tests pass and new test coverage added.

* test(e2e): Add comprehensive tag-aware expand testing to verify tag corruption fix - Add new test section for feature-expand tag creation and testing - Verify tag preservation during expand, force expand, and expand --all operations - Test that master tag remains intact and feature-expand tag receives subtasks correctly - Fix file path references to use correct .taskmaster/tasks/tasks.json location - Fix config file check to use .taskmaster/config.json instead of .taskmasterconfig - All tag corruption verification tests pass successfully in E2E test

* fix(changeset): Update E2E test improvements changeset to properly reflect tag corruption fix verification

* chore(changeset): combine duplicate changesets for expand tag corruption fix

Merge eighty-breads-wonder.md into bright-llamas-enter.md to consolidate
the expand command fix and its comprehensive E2E testing enhancements
into a single changeset entry.

* Delete .changeset/eighty-breads-wonder.md

* Version Packages

* chore: fix package.json

* fix(expand): Enhance context handling in expandAllTasks function
- Added `tag` to context destructuring for better context management.
- Updated `readJSON` call to include `contextTag` for improved data integrity.
- Ensured the correct tag is passed during task expansion to prevent tag corruption.

---------

Co-authored-by: Parththipan Thaniperumkarunai <parththipan.thaniperumkarunai@milkmonkey.de>
Co-authored-by: Parthy <52548018+mm-parthy@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Ralph Khreish
2025-06-20 16:12:40 +03:00
committed by GitHub
parent 04f44a2d3d
commit 5da5b59bde
10 changed files with 1974 additions and 58 deletions

View File

@@ -625,19 +625,38 @@ describe('MCP Server Direct Functions', () => {
// For successful cases, record that functions were called but don't make real calls
mockEnableSilentMode();
// Mock expandAllTasks
// Mock expandAllTasks - now returns a structured object instead of undefined
const mockExpandAll = jest.fn().mockImplementation(async () => {
// Just simulate success without any real operations
return undefined; // expandAllTasks doesn't return anything
// Return the new structured response that matches the actual implementation
return {
success: true,
expandedCount: 2,
failedCount: 0,
skippedCount: 1,
tasksToExpand: 3,
telemetryData: {
timestamp: new Date().toISOString(),
commandName: 'expand-all-tasks',
totalCost: 0.05,
totalTokens: 1000,
inputTokens: 600,
outputTokens: 400
}
};
});
// Call mock expandAllTasks
await mockExpandAll(
args.num,
args.research || false,
args.prompt || '',
args.force || false,
{ mcpLog: mockLogger, session: options.session }
// Call mock expandAllTasks with the correct signature
const result = await mockExpandAll(
args.file, // tasksPath
args.num, // numSubtasks
args.research || false, // useResearch
args.prompt || '', // additionalContext
args.force || false, // force
{
mcpLog: mockLogger,
session: options.session,
projectRoot: args.projectRoot
}
);
mockDisableSilentMode();
@@ -645,13 +664,14 @@ describe('MCP Server Direct Functions', () => {
return {
success: true,
data: {
message: 'Successfully expanded all pending tasks with subtasks',
message: `Expand all operation completed. Expanded: ${result.expandedCount}, Failed: ${result.failedCount}, Skipped: ${result.skippedCount}`,
details: {
numSubtasks: args.num,
research: args.research || false,
prompt: args.prompt || '',
force: args.force || false
}
expandedCount: result.expandedCount,
failedCount: result.failedCount,
skippedCount: result.skippedCount,
tasksToExpand: result.tasksToExpand
},
telemetryData: result.telemetryData
}
};
}
@@ -671,10 +691,13 @@ describe('MCP Server Direct Functions', () => {
// Assert
expect(result.success).toBe(true);
expect(result.data.message).toBe(
'Successfully expanded all pending tasks with subtasks'
);
expect(result.data.details.numSubtasks).toBe(3);
expect(result.data.message).toMatch(/Expand all operation completed/);
expect(result.data.details.expandedCount).toBe(2);
expect(result.data.details.failedCount).toBe(0);
expect(result.data.details.skippedCount).toBe(1);
expect(result.data.details.tasksToExpand).toBe(3);
expect(result.data.telemetryData).toBeDefined();
expect(result.data.telemetryData.commandName).toBe('expand-all-tasks');
expect(mockEnableSilentMode).toHaveBeenCalled();
expect(mockDisableSilentMode).toHaveBeenCalled();
});
@@ -695,7 +718,8 @@ describe('MCP Server Direct Functions', () => {
// Assert
expect(result.success).toBe(true);
expect(result.data.details.research).toBe(true);
expect(result.data.details.expandedCount).toBe(2);
expect(result.data.telemetryData).toBeDefined();
expect(mockEnableSilentMode).toHaveBeenCalled();
expect(mockDisableSilentMode).toHaveBeenCalled();
});
@@ -715,7 +739,8 @@ describe('MCP Server Direct Functions', () => {
// Assert
expect(result.success).toBe(true);
expect(result.data.details.force).toBe(true);
expect(result.data.details.expandedCount).toBe(2);
expect(result.data.telemetryData).toBeDefined();
expect(mockEnableSilentMode).toHaveBeenCalled();
expect(mockDisableSilentMode).toHaveBeenCalled();
});
@@ -735,11 +760,77 @@ describe('MCP Server Direct Functions', () => {
// Assert
expect(result.success).toBe(true);
expect(result.data.details.prompt).toBe(
'Additional context for subtasks'
);
expect(result.data.details.expandedCount).toBe(2);
expect(result.data.telemetryData).toBeDefined();
expect(mockEnableSilentMode).toHaveBeenCalled();
expect(mockDisableSilentMode).toHaveBeenCalled();
});
test('should handle case with no eligible tasks', async () => {
// Arrange
const args = {
projectRoot: testProjectRoot,
file: testTasksPath,
num: 3
};
// Act - Mock the scenario where no tasks are eligible for expansion
async function testNoEligibleTasks(args, mockLogger, options = {}) {
mockEnableSilentMode();
const mockExpandAll = jest.fn().mockImplementation(async () => {
return {
success: true,
expandedCount: 0,
failedCount: 0,
skippedCount: 0,
tasksToExpand: 0,
telemetryData: null,
message: 'No tasks eligible for expansion.'
};
});
const result = await mockExpandAll(
args.file,
args.num,
false,
'',
false,
{
mcpLog: mockLogger,
session: options.session,
projectRoot: args.projectRoot
},
'json'
);
mockDisableSilentMode();
return {
success: true,
data: {
message: result.message,
details: {
expandedCount: result.expandedCount,
failedCount: result.failedCount,
skippedCount: result.skippedCount,
tasksToExpand: result.tasksToExpand
},
telemetryData: result.telemetryData
}
};
}
const result = await testNoEligibleTasks(args, mockLogger, {
session: mockSession
});
// Assert
expect(result.success).toBe(true);
expect(result.data.message).toBe('No tasks eligible for expansion.');
expect(result.data.details.expandedCount).toBe(0);
expect(result.data.details.tasksToExpand).toBe(0);
expect(result.data.telemetryData).toBeNull();
});
});
});